<!DOCTYPE html><html lang="zh-CN"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=2"><meta name="theme-color" content="#222"><meta name="generator" content="Hexo 3.9.0"><link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png"><link rel="icon" type="image/png" sizes="32x32" href="/favicon.ico"><link rel="icon" type="image/png" sizes="16x16" href="/favicon.ico"><link rel="mask-icon" href="/images/logo.svg" color="#222"><meta name="baidu-site-verification" content="WIIeufYjj6"><link rel="stylesheet" href="/css/main.css"><link rel="stylesheet" href="/lib/font-awesome/css/font-awesome.min.css"><script id="hexo-configurations">var NexT=window.NexT||{},CONFIG={hostname:new URL("http://winddoing.github.io").hostname,root:"/",scheme:"Mist",version:"7.7.0",exturl:!1,sidebar:{position:"right",width:300,display:"post",padding:18,offset:12,onmobile:!1},copycode:{enable:!0,show_result:!1,style:"flat"},back2top:{enable:!0,sidebar:!1,scrollpercent:!1},bookmark:{enable:!1,color:"#222",save:"auto"},fancybox:!1,mediumzoom:!1,lazyload:!1,pangu:!1,comments:{style:"tabs",active:"livere",storage:!0,lazyload:!1,nav:null,activeClass:"livere"},algolia:{appID:"OWFNL0AG1F",apiKey:"796a4b50e2653330cac4e0f8b1f8ade4",indexName:"winddoing",hits:{per_page:10},labels:{input_placeholder:"输入关键词",hits_empty:"没有找到与「${query}」相关的内容",hits_stats:"${hits} 条相关记录，共耗时 ${time} ms"}},localsearch:{enable:!0,trigger:"auto",top_n_per_article:1,unescape:!1,preload:!1},path:"search.xml",motion:{enable:!0,async:!1,transition:{post_block:"fadeIn",post_header:"slideDownIn",post_body:"slideDownIn",coll_header:"slideLeftIn",sidebar:"slideUpIn"}}}</script><meta name="description" content="程序员的自我修养  链接、装载与库   温故而知新内存不够怎么办 任何将计算机上有限的物理内存分配给多个程序使用  如果采用直接使用物理内存空间执行程序,存在一下问题:  地址空间不隔离  所有程序直接访问物理地址,程序所使用的内存空间不是相互隔离的,程序容易被有意或者无意的修改,使其崩溃.   内存使用率低  整个程序都加载到内存中占用大量空间,执行新的程序空间不足时,也需要换入换出大量数据."><meta name="keywords" content="Hexo, NexT, Winddoing,嵌入式,Linux,shell,C语言,网络， 个人博客，专注嵌入式系统开发， mips"><meta property="og:type" content="website"><meta property="og:title" content="程序员的自我修养"><meta property="og:url" content="http://winddoing.github.io/books/程序员的自我修养/index.html"><meta property="og:site_name" content="Winddoing&#39;s Blog"><meta property="og:description" content="程序员的自我修养  链接、装载与库   温故而知新内存不够怎么办 任何将计算机上有限的物理内存分配给多个程序使用  如果采用直接使用物理内存空间执行程序,存在一下问题:  地址空间不隔离  所有程序直接访问物理地址,程序所使用的内存空间不是相互隔离的,程序容易被有意或者无意的修改,使其崩溃.   内存使用率低  整个程序都加载到内存中占用大量空间,执行新的程序空间不足时,也需要换入换出大量数据."><meta property="og:locale" content="zh-CN"><meta property="og:image" content="http://winddoing.github.io/images/2018/12/page_fault_flow.png"><meta property="og:image" content="http://winddoing.github.io/images/2018/12/gcc编译过程.png"><meta property="og:image" content="http://winddoing.github.io/images/2019/03/程序员的自我修养_page_map.png"><meta property="og:updated_time" content="2020-07-04T09:34:15.256Z"><meta name="twitter:card" content="summary"><meta name="twitter:title" content="程序员的自我修养"><meta name="twitter:description" content="程序员的自我修养  链接、装载与库   温故而知新内存不够怎么办 任何将计算机上有限的物理内存分配给多个程序使用  如果采用直接使用物理内存空间执行程序,存在一下问题:  地址空间不隔离  所有程序直接访问物理地址,程序所使用的内存空间不是相互隔离的,程序容易被有意或者无意的修改,使其崩溃.   内存使用率低  整个程序都加载到内存中占用大量空间,执行新的程序空间不足时,也需要换入换出大量数据."><meta name="twitter:image" content="http://winddoing.github.io/images/2018/12/page_fault_flow.png"><link rel="canonical" href="http://winddoing.github.io/books/程序员的自我修养/"><script id="page-configurations">CONFIG.page={sidebar:"",isHome:!1,isPost:!1}</script><link rel="stylesheet" href="//cdn.jsdelivr.net/gh/theme-next/theme-next-needmoreshare2@1/needsharebutton.min.css"><style>#needsharebutton-postbottom{cursor:pointer;height:26px;margin-top:10px;position:relative}#needsharebutton-postbottom .btn{border:1px solid $btn-default-border-color;border-radius:3px;display:initial;padding:1px 4px}</style><title>程序员的自我修养 | Winddoing's Blog</title><script>var _hmt=_hmt||[];!function(){var e=document.createElement("script");e.src="https://hm.baidu.com/hm.js?38fa95924670925239ef842cb0c8722b";var t=document.getElementsByTagName("script")[0];t.parentNode.insertBefore(e,t)}()</script><noscript><style>.sidebar-inner,.use-motion .brand,.use-motion .collection-header,.use-motion .comments,.use-motion .menu-item,.use-motion .pagination,.use-motion .post-block,.use-motion .post-body,.use-motion .post-header{opacity:initial}.use-motion .site-subtitle,.use-motion .site-title{opacity:initial;top:initial}.use-motion .logo-line-before i{left:initial}.use-motion .logo-line-after i{right:initial}</style></noscript></head><body itemscope itemtype="http://schema.org/WebPage"><div class="container use-motion"><div class="headband"></div><header class="header" itemscope itemtype="http://schema.org/WPHeader"><div class="header-inner"><div class="site-brand-container"><div class="site-meta"><div><a href="/" class="brand" rel="start"><span class="logo-line-before"><i></i></span> <span class="site-title">Winddoing's Blog</span><span class="logo-line-after"><i></i></span></a></div><h1 class="site-subtitle" itemprop="description">Follow Excellent, Success will Chase you</h1></div><div class="site-nav-toggle"><div class="toggle" aria-label="切换导航栏"><span class="toggle-line toggle-line-first"></span><span class="toggle-line toggle-line-middle"></span><span class="toggle-line toggle-line-last"></span></div></div></div><nav class="site-nav"><ul id="menu" class="menu"><li class="menu-item menu-item-home"><a href="/" rel="section"><i class="fa fa-fw fa-home"></i> 首页</a></li><li class="menu-item menu-item-tags"><a href="/tags/" rel="section"><i class="fa fa-fw fa-tags"></i> 标签</a></li><li class="menu-item menu-item-categories"><a href="/categories/" rel="section"><i class="fa fa-fw fa-th"></i> 分类</a></li><li class="menu-item menu-item-archives"><a href="/archives/" rel="section"><i class="fa fa-fw fa-archive"></i> 归档</a></li><li class="menu-item menu-item-about"><a href="/about/" rel="section"><i class="fa fa-fw fa-user"></i> 关于</a></li><li class="menu-item menu-item-search"><a role="button" class="popup-trigger"><i class="fa fa-search fa-fw"></i> 搜索</a></li></ul></nav><div class="site-search"><div class="popup search-popup"><div class="search-header"><span class="search-icon"><i class="fa fa-search"></i></span><div class="search-input-container"> <input autocomplete="off" autocorrect="off" autocapitalize="none" placeholder="搜索..." spellcheck="false" type="text" id="search-input"></div><span class="popup-btn-close"><i class="fa fa-times-circle"></i></span></div><div id="search-result"></div></div><div class="search-pop-overlay"></div></div></div></header><div class="back-to-top"><i class="fa fa-arrow-up"></i> <span>0%</span></div><main class="main"><div class="main-inner"><div class="content-wrap"><div class="content"><div class="posts-expand"><div class="post-block" lang="zh-CN"><header class="post-header"><h2 class="post-title" itemprop="name headline">程序员的自我修养</h2><div class="post-meta"><ul class="breadcrumb"><li><a href="/books/">BOOKS</a></li><li>程序员的自我修养</li></ul></div></header><div class="post-body"><blockquote class="blockquote-center"><p>程序员的自我修养 链接、装载与库</p></blockquote><h1 id="温故而知新"><a href="#温故而知新" class="headerlink" title="温故而知新"></a>温故而知新</h1><h2 id="内存不够怎么办"><a href="#内存不够怎么办" class="headerlink" title="内存不够怎么办"></a>内存不够怎么办</h2><blockquote><p>任何将计算机上有限的物理内存分配给多个程序使用</p></blockquote><p>如果采用直接使用物理内存空间执行程序,存在一下问题:</p><ol><li><p>地址空间不隔离</p><blockquote><p>所有程序直接访问物理地址,程序所使用的内存空间不是相互隔离的,程序容易被有意或者无意的修改,使其崩溃.</p></blockquote></li><li><p>内存使用率低</p><blockquote><p>整个程序都加载到内存中占用大量空间,执行新的程序空间不足时,也需要换入换出大量数据.</p></blockquote></li><li><p>程序运行地址不确定</p><blockquote><p>程序在编写时,其实是编译成可执行文件时,它访问数据和指令跳转时的目的地址很多都是固定的,地址不确定会造成很大麻烦.</p></blockquote></li></ol><p> <strong>解决方法: <code>增加中间层</code></strong> 使用一种间接的地址访问方法 — <code>虚拟地址(Virtual Address)</code></p><p>把程序给出的地址看作是一种虚拟地址,然后通过某些映射方法,将这个虚拟地址转换成实际的物理地址.</p><h2 id="隔离"><a href="#隔离" class="headerlink" title="隔离"></a>隔离</h2><p>地址空间: 所谓地址空间是个比较抽象的概念,你可以把它想象成一个很大的数组,每个数组的元素是一个字节,而这个数组大小由地址空间的地址长度(地址线的个数)决定,比如32位地址空间大小为2^32=4 294 967 296字节,即4G</p><p>虚拟地址空间:指虚拟的,人们想象出来的地址空间,其实它并不存在,每一个进程都有自己独立的虚拟地址空间,而且每个进程只能访问自己的地址空间,这样就有效的做到了<code>进程的隔离</code></p><h3 id="分段-Segmentation"><a href="#分段-Segmentation" class="headerlink" title="分段(Segmentation)"></a>分段(Segmentation)</h3><blockquote><p>基本思路: 把一段与程序所需要的内存空间大小的虚拟空间映射到某个地址空间</p></blockquote><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line">                                           +-------------------+</span><br><span class="line">                                           |                   |</span><br><span class="line">                                           |                   |</span><br><span class="line">0x0640 0000+----------------XX             |                   |</span><br><span class="line">           |                | XXXX         |                   |</span><br><span class="line">           |                |    XXXXXXXXXXX-------------------+0x7000 0000</span><br><span class="line">           |                |              |                   |         +</span><br><span class="line">           |                |              |                   |         |</span><br><span class="line">           |                |              |                   |         |</span><br><span class="line">           |  Virtual Addr  |              |                   |         |</span><br><span class="line">           |  Space of B    |              |      Physical     |         |</span><br><span class="line">           |                |              |  Address Space    |         |</span><br><span class="line">           |                |    map       |      of B         |         |</span><br><span class="line">           |                |              |                   |         v</span><br><span class="line">           |                |              |                   |       100MB</span><br><span class="line">           |                |              |                   |         ^</span><br><span class="line">           |                |              |                   |         |</span><br><span class="line">           |                |              |                   |         |</span><br><span class="line">           |                |              |                   |         |</span><br><span class="line">0x0000 0000+----------------XXX            |                   |         |</span><br><span class="line">                               XXXX        |                   |         |</span><br><span class="line">                                  XXXX     |                   |         +</span><br><span class="line">0x00A0 0000+----------------XX       XXXXXX+-------------------+0x00c0 0000</span><br><span class="line">           |  Virtual Addr  |XXX           |                   |</span><br><span class="line">           |  Apace of A    |  XXXXXXXXXXXXX-------------------+0x00B0 0000</span><br><span class="line">           |                |              | Physical Address  |</span><br><span class="line">0x0000 0000+----------------X    map       |    Space of A     |        10MB</span><br><span class="line">                            XXXXX          |                   |</span><br><span class="line">                                 XXXXXXXXXXX-------------------+0x0010 0000</span><br><span class="line">                                           |                   |</span><br><span class="line">                                           |                   |</span><br><span class="line">                                           +-------------------+0x0000 0000</span><br></pre></td></tr></table></figure><p>分段可以解决第一个和第三个问题.</p><ol><li><strong>地址隔离</strong>: 程序A和程序被映射到两块不同的物理空间区域，他们之间没有任何重叠．如果程序Ａ访问访问虚拟空间的地址超过了0x00A0 0000这个范围，那么硬件就会判断这是一个非法访问，拒绝这个地址请求，并将这个请求报告给操作系统或监控程序，由它决定处理．</li><li><strong>固定程序运行地址</strong>：每个程序而言不需要关心虚拟地址与物理地址之间的映射，相当于其透明的，程序编写只需要按照从地址0x0000 0000到0x00A0 0000来编写程序，放置变量，程序不需要重定位．</li></ol><h3 id="分页-Paging"><a href="#分页-Paging" class="headerlink" title="分页(Paging)"></a>分页(Paging)</h3><p>分段是对整个程序而言，其换入换出将增加大量的磁盘访问操作,从而严重影响速度,因此利用<code>程序的局部性原理</code>使用更小粒度的内存分割和映射方法,就是<code>分页(Paging)</code></p><blockquote><p>分页的基本方法是把地址空间人为的等分成固定大小的页,每一页的大小由硬件决定,或硬件支持多种大小的页,由操作系统选择决定页的大小</p></blockquote><p>页大小: MIPS 8K</p><h4 id="Page-Fault"><a href="#Page-Fault" class="headerlink" title="Page Fault"></a>Page Fault</h4><p><img src="/images/2018/12/page_fault_flow.png" alt="page_fault_flow"></p><blockquote><p>进程的虚拟地址空间按<code>页</code>进行分割后,把常用的数据和代码页装载到内存中,把不常用的数据和代码页保存在磁盘中,当需要用到的时候再把它从磁盘中取出来即可.</p></blockquote><p>假设进程<code>Process1</code>,<code>Process2</code>,他们进程中的部分虚拟页面被映射到了物理页面,比如VP0,VP1和VP7映射到PP0,PP2和PP3,但是一部分在磁盘中比如DP0,DP1.</p><ul><li>如果程序运行时,只是有到了VP0,VP1和VP7的页空间,将不存在任何异常,程序正常运行</li><li>如果程序运行是,访问到了VP2和VP3的页空间,由于这两个页不在内存中,在磁盘中DP0和DP1中,因此硬件会捕获到这个信息,这就是<code>段错误(Page Fault)</code></li></ul><h4 id="页映射–数据保护"><a href="#页映射–数据保护" class="headerlink" title="页映射–数据保护"></a>页映射–数据保护</h4><blockquote><p>在页映射时,可以对每个页设置权限属性,谁可以修改,谁可以访问,而只有操作系统有修改页属性的权利</p></blockquote><blockquote><p><strong>Linux内核中如何实现???</strong></p></blockquote><h4 id="MMU-Memory-Management-Unit"><a href="#MMU-Memory-Management-Unit" class="headerlink" title="MMU (Memory Management Unit)"></a>MMU (Memory Management Unit)</h4><blockquote><p>CPU内部集成的一个硬件部件</p></blockquote><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">+-------------+               +-----------+              +----------------+</span><br><span class="line">|             |    Virtual    |           |   Physical   |    Physical    |</span><br><span class="line">|     CPU     +---------------&gt;    MMU    +--------------&gt;     Memory     |</span><br><span class="line">|             |    Address    |           |   Address    |                |</span><br><span class="line">+-------------+               +-----------+              +----------------+</span><br></pre></td></tr></table></figure><ul><li>所谓CPU的<code>总线地址</code>大多数情况下就是指<code>物理地址</code></li></ul><h2 id="线程-–-Thread"><a href="#线程-–-Thread" class="headerlink" title="线程 – Thread"></a>线程 – Thread</h2><blockquote><p><strong>线程</strong>: <code>执行流的最小单元</code>,有时也称<code>轻量级进程</code>,</p></blockquote><ul><li>一个标准的线程由<code>线程ID</code>,<code>当前指令指针(PC)</code>,<code>寄存器集合</code>和<code>堆栈</code>组成</li><li>通常,一个进程由一个到多个线程组成,各个线程之间共享程序的内存空间(包括代码段,数据段,堆等)及一些进程级资源(如打开文件和信号)</li></ul><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line">+--------------------------------------------------------------+</span><br><span class="line">| +----------------------------------------------------------+ |</span><br><span class="line">| |    代码     |     数据     |    进程空间      |   打开文件   | |</span><br><span class="line">| +------------+--------------+----------------+-------------+ |</span><br><span class="line">|                                                              |</span><br><span class="line">|  +-------------+     +-------------+    +--------------+     |</span><br><span class="line">|  | +---------+ |     | +---------+ |    | +----------+ |     |</span><br><span class="line">|  | |  寄存器  | |     | | 寄存器   | |    | |  寄存器   | |     |</span><br><span class="line">|  | +---------+ |     | +---------+ |    | +----------+ |     |</span><br><span class="line">|  |             |     |             |    |              |     |</span><br><span class="line">|  | +---------+ |     | +---------+ |    | +----------+ |     |</span><br><span class="line">|  | |   栈    | |     | |    栈    | |    | |   栈     | |     |</span><br><span class="line">|  | +---------+ |     | +---------+ |    | +----------+ |     |</span><br><span class="line">|  |             |     |             |    |              |     |</span><br><span class="line">|  |             |     |             |    |              |     |</span><br><span class="line">|  |             |     |             |    |              |     |</span><br><span class="line">|  |             |     |             |    |              |     |</span><br><span class="line">|  |             |     |             |    |              |     |</span><br><span class="line">|  |             |     |             |    |              |     |</span><br><span class="line">|  |             |     |             |    |              |     |</span><br><span class="line">|  |             |     |             |    |              |     |</span><br><span class="line">|  |             |     |             |    |              |     |</span><br><span class="line">|  |             |     |             |    |              |     |</span><br><span class="line">|  | Main Thread |     |   Thread 1  |    |   Thread 2   |     |</span><br><span class="line">|  +-------------+     +-------------+    +--------------+     |</span><br><span class="line">+--------------------------------------------------------------+</span><br></pre></td></tr></table></figure><h3 id="线程的访问权限"><a href="#线程的访问权限" class="headerlink" title="线程的访问权限"></a>线程的访问权限</h3><table><thead><tr><th style="text-align:center">线程私有</th><th style="text-align:left">线程之间共享(进程所有)</th></tr></thead><tbody><tr><td style="text-align:center">局部变量</td><td style="text-align:left">全局变量</td></tr><tr><td style="text-align:center">函数参数</td><td style="text-align:left">堆上的数据</td></tr><tr><td style="text-align:center">TLS数据</td><td style="text-align:left">函数里的静态变量</td></tr><tr><td style="text-align:center">-</td><td style="text-align:left">程序代码,任何线程都有权利读取并执行任何代码</td></tr><tr><td style="text-align:center">-</td><td style="text-align:left">打开的文件, A线程打开的文件可以由线程B读写</td></tr></tbody></table><h3 id="线程调度"><a href="#线程调度" class="headerlink" title="线程调度"></a>线程调度</h3><p>线程的三种状态:</p><ul><li><strong>运行(Runing)</strong>: 此时线程正在执行</li><li><strong>就绪(Ready)</strong>: 此时线程可以立刻运行,但是CPU已经被占用</li><li><strong>等待(Wait)</strong>: 此时线程正在等待某一事件(通常指I/O或同步)发生,无法执行</li></ul><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">                 无运行线程,且本线程被选中</span><br><span class="line">     +------------------------------------------+</span><br><span class="line">     |                                          |</span><br><span class="line">     |                                          |</span><br><span class="line">     |                                          |</span><br><span class="line">     |                                          |</span><br><span class="line">+----v-----+                              +-----+-----+</span><br><span class="line">|          |         时间片用尽             |           |</span><br><span class="line">|  Runing  +------------------------------&gt;   Ready   |</span><br><span class="line">|          |                              |           |</span><br><span class="line">+----+-----+                              +-----^-----+</span><br><span class="line">     |                                          |</span><br><span class="line">     |                                          |</span><br><span class="line"> 开始等待                                      等待结束</span><br><span class="line">     |                                          |</span><br><span class="line">     |             +----------+                 |</span><br><span class="line">     |             |          |                 |</span><br><span class="line">     +-------------&gt;   Wait   +-----------------+</span><br><span class="line">                   |          |</span><br><span class="line">                   +----------+</span><br></pre></td></tr></table></figure><h3 id="线程安全"><a href="#线程安全" class="headerlink" title="线程安全"></a>线程安全</h3><blockquote><p>多线程并发执行时,数据的一致性</p></blockquote><h4 id="竞争与原子操作"><a href="#竞争与原子操作" class="headerlink" title="竞争与原子操作"></a>竞争与原子操作</h4><ul><li>原子(Atomic):指单指令操作</li></ul><h4 id="锁与同步"><a href="#锁与同步" class="headerlink" title="锁与同步"></a>锁与同步</h4><ol><li><strong>二元信号量</strong>: 最简单的一种锁,它只有两种状态:<code>占用</code>与<code>非占用</code>.适用只能被唯一一个线程独占访问的资源</li><li><strong>互斥量(Mutex)</strong>:与二元信号量类似,资源仅同时允许一个线程访问,但是互斥量是哪个线程获取互斥量,必须哪个线程释放互斥量</li><li><strong>临界区</strong>:互斥量保护的范围,就是临界区</li><li><strong>读写锁</strong>:读写锁两种获取方式<code>共享的(Shared)</code>和<code>独占的(Exclusive)</code>,适用频繁读取,只是偶尔写入的场景</li><li><strong>条件变量</strong>: 类似于一个栅栏,一个条件变量可以被多个线程等待,当时间发生时(条件变量被唤醒),所有线程可以一起恢复执行</li></ol><h3 id="可重入-Reentrant-与线程安全"><a href="#可重入-Reentrant-与线程安全" class="headerlink" title="可重入(Reentrant)与线程安全"></a>可重入(Reentrant)与线程安全</h3><p>一个函数可重入的<strong>特点</strong>:</p><ol><li>不使用任何(局部)静态或全局的非const变量</li><li>不使用任何(局部)静态或全局的非const变量的变量</li><li>仅依赖调用函数提供的参数</li><li>不依赖任何单个资源的锁(mutex等)</li><li>不调用任何不可重入函数</li></ol><h3 id="过度优化"><a href="#过度优化" class="headerlink" title="过度优化"></a>过度优化</h3><p>过度优化带来的问题:</p><ol><li><code>编译器调整顺序</code>:编译器为提高执行速度,将一些结果保存临时寄存器中</li><li><code>CPU动态调度换序</code>: CPU的动态调度,在执行过程中,为了提高效率,几个互补相关的指令,可能被交换执行,或者同时执行</li></ol><p>解决方法:</p><ol><li><code>volatile</code>关键字阻止过度优化<ul><li>阻止编译器为了提高速度将一个变量缓存到寄存器不写回</li><li>阻止编译器调整操作volatile变量的指令顺序</li></ul></li><li><code>barrier</code>指令, 一条barrier指令会阻止CPU将该指令之前的指令交换到barrier指令之后,也就是说CPU执行到barrier指令时,前面的所有指令已经执行完成</li></ol><hr><h1 id="静态链接"><a href="#静态链接" class="headerlink" title="静态链接"></a>静态链接</h1><h2 id="GCC编译过程"><a href="#GCC编译过程" class="headerlink" title="GCC编译过程"></a>GCC编译过程</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$gcc hello.c</span><br></pre></td></tr></table></figure><blockquote><p>编译的过程可以分解成4个步骤:<code>预处理(Prepressing)</code>,<code>编译(Compilation)</code>,<code>汇编(Assembly)</code>和<code>链接(Linking)</code></p></blockquote><p><img src="/images/2018/12/gcc编译过程.png" alt="Gcc编译过程"></p><h3 id="预编译-Prepressing"><a href="#预编译-Prepressing" class="headerlink" title="预编译-Prepressing"></a>预编译-Prepressing</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">gcc -E hello.c -o hello.i</span><br><span class="line">或</span><br><span class="line">cpp hello.c &gt; hello.i</span><br></pre></td></tr></table></figure><p>预编译过程主要处理源代码中以<code>&quot;#&quot;</code>开始的预编译指令.比如”#include”, “#define”</p><p>处理规则:</p><ul><li>将所有的<code>&quot;#define&quot;</code>删除,并且展开所有的宏定义</li><li>处理所有条件预编译指令,比如<code>&quot;#if&quot;</code>, <code>&quot;ifdef&quot;</code>, <code>&quot;#elif&quot;</code>, <code>&quot;#else&quot;</code>, <code>&quot;#endif&quot;</code></li><li>处理<code>&quot;#include&quot;</code>预编译指令,将包含的文件插入到该预编译指令的位置,注意这个过程是递归进行的,也就是说被包含的文件可能还包含其他文件</li><li>删除所有注释<code>&quot;//&quot;</code>和<code>&quot;/* */&quot;</code></li><li>添加行号和文件标识,比如#2 “hello.c” 2,以便于编译时编译器产生调试用的行号信息及用于编译时产生编译错误和警告时能够显示行号</li><li>保留所有的<code>&quot;#pragma&quot;</code>编译器指令,因为编译器需要使用它们</li></ul><h3 id="编译-Compilation"><a href="#编译-Compilation" class="headerlink" title="编译-Compilation"></a>编译-Compilation</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">gcc -S hello.i -o hello.s</span><br></pre></td></tr></table></figure><p>编译的过程就是把预处理完的文件进行一系列词法分析,语法分析,语义分析及优化后生成相应的汇编代码文件</p><h3 id="汇编-Assembly"><a href="#汇编-Assembly" class="headerlink" title="汇编-Assembly"></a>汇编-Assembly</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">gcc -c hello.s -o hello.o</span><br><span class="line">或</span><br><span class="line">as hello.s -o hello.o</span><br></pre></td></tr></table></figure><h3 id="链接-Linking"><a href="#链接-Linking" class="headerlink" title="链接-Linking"></a>链接-Linking</h3><p>通过<code>ld</code>链接一些必要的文件使其生成一个可执行文件</p><h3 id="示例-Gcc-7-3-0"><a href="#示例-Gcc-7-3-0" class="headerlink" title="示例: Gcc 7.3.0"></a>示例: Gcc 7.3.0</h3><p>环境编译器及系统: <code>gcc version 7.3.0 (Ubuntu 7.3.0-27ubuntu1~18.04)</code></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">gcc hello.c -v</span><br></pre></td></tr></table></figure><ul><li><code>-v</code>: 显示编译器调用处理的细节</li><li><code>-save-temps</code>: 不删除中间临时文件,如*.i, *.s</li></ul><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br></pre></td><td class="code"><pre><span class="line">=====&gt;$gcc hello.c -v  -save-temps</span><br><span class="line">Using built-in specs.</span><br><span class="line">COLLECT_GCC=gcc</span><br><span class="line">COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/7/lto-wrapper</span><br><span class="line">OFFLOAD_TARGET_NAMES=nvptx-none</span><br><span class="line">OFFLOAD_TARGET_DEFAULT=1</span><br><span class="line">Target: x86_64-linux-gnu</span><br><span class="line">Configured with: ../src/configure -v --with-pkgversion=&apos;Ubuntu 7.3.0-27ubuntu1~18.04&apos; --with-bugurl=file:///usr/share/doc/gcc-7/README.Bugs --enable-languages=c,ada,c++,go,brig,d,fortran,objc,obj-c++ --prefix=/usr --with-gcc-major-version-only --program-suffix=-7 --program-prefix=x86_64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-libmpx --enable-plugin --enable-default-pie --with-system-zlib --with-target-system-zlib --enable-objc-gc=auto --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-offload-targets=nvptx-none --without-cuda-driver --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu</span><br><span class="line">Thread model: posix</span><br><span class="line">gcc version 7.3.0 (Ubuntu 7.3.0-27ubuntu1~18.04)</span><br><span class="line">COLLECT_GCC_OPTIONS=&apos;-v&apos; &apos;-save-temps&apos; &apos;-mtune=generic&apos; &apos;-march=x86-64&apos;</span><br><span class="line"> /usr/lib/gcc/x86_64-linux-gnu/7/cc1 -E -quiet -v -imultiarch x86_64-linux-gnu hello.c -mtune=generic -march=x86-64 -fpch-preprocess -fstack-protector-strong -Wformat -Wformat-security -o hello.i</span><br><span class="line">ignoring nonexistent directory &quot;/usr/local/include/x86_64-linux-gnu&quot;</span><br><span class="line">ignoring nonexistent directory &quot;/usr/lib/gcc/x86_64-linux-gnu/7/../../../../x86_64-linux-gnu/include&quot;</span><br><span class="line">#include &quot;...&quot; search starts here:</span><br><span class="line">#include &lt;...&gt; search starts here:</span><br><span class="line"> /usr/lib/gcc/x86_64-linux-gnu/7/include</span><br><span class="line"> /usr/local/include</span><br><span class="line"> /usr/lib/gcc/x86_64-linux-gnu/7/include-fixed</span><br><span class="line"> /usr/include/x86_64-linux-gnu</span><br><span class="line"> /usr/include</span><br><span class="line">End of search list.</span><br><span class="line">COLLECT_GCC_OPTIONS=&apos;-v&apos; &apos;-save-temps&apos; &apos;-mtune=generic&apos; &apos;-march=x86-64&apos;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> /usr/lib/gcc/x86_64-linux-gnu/7/cc1 -fpreprocessed hello.i -quiet -dumpbase hello.c -mtune=generic -march=x86-64 -auxbase hello -version -fstack-protector-strong -Wformat -Wformat-security -o hello.s</span><br><span class="line">GNU C11 (Ubuntu 7.3.0-27ubuntu1~18.04) version 7.3.0 (x86_64-linux-gnu)</span><br><span class="line">	compiled by GNU C version 7.3.0, GMP version 6.1.2, MPFR version 4.0.1, MPC version 1.1.0, isl version isl-0.19-GMP</span><br><span class="line"></span><br><span class="line">GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072</span><br><span class="line">GNU C11 (Ubuntu 7.3.0-27ubuntu1~18.04) version 7.3.0 (x86_64-linux-gnu)</span><br><span class="line">	compiled by GNU C version 7.3.0, GMP version 6.1.2, MPFR version 4.0.1, MPC version 1.1.0, isl version isl-0.19-GMP</span><br><span class="line"></span><br><span class="line">GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072</span><br><span class="line">Compiler executable checksum: c8081a99abb72bbfd9129549110a350c</span><br><span class="line">COLLECT_GCC_OPTIONS=&apos;-v&apos; &apos;-save-temps&apos; &apos;-mtune=generic&apos; &apos;-march=x86-64&apos;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> as -v --64 -o hello.o hello.s</span><br><span class="line">GNU assembler version 2.30 (x86_64-linux-gnu) using BFD version (GNU Binutils for Ubuntu) 2.30</span><br><span class="line">COMPILER_PATH=/usr/lib/gcc/x86_64-linux-gnu/7/:/usr/lib/gcc/x86_64-linux-gnu/7/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/7/:/usr/lib/gcc/x86_64-linux-gnu/</span><br><span class="line">LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/7/:/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/7/../../../../lib/:/lib/x86_64-linux-gnu/:/lib/../lib/:/usr/lib/x86_64-linux-gnu/:/usr/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/7/../../../:/lib/:/usr/lib/</span><br><span class="line">COLLECT_GCC_OPTIONS=&apos;-v&apos; &apos;-save-temps&apos; &apos;-mtune=generic&apos; &apos;-march=x86-64&apos;</span><br><span class="line"></span><br><span class="line"> /usr/lib/gcc/x86_64-linux-gnu/7/collect2 -plugin /usr/lib/gcc/x86_64-linux-gnu/7/liblto_plugin.so -plugin-opt=/usr/lib/gcc/x86_64-linux-gnu/7/lto-wrapper -plugin-opt=-fresolution=hello.res -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lc -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s --sysroot=/ --build-id --eh-frame-hdr -m elf_x86_64 --hash-style=gnu --as-needed -dynamic-linker /lib64/ld-linux-x86-64.so.2 -pie -z now -z relro /usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/Scrt1.o /usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/7/crtbeginS.o -L/usr/lib/gcc/x86_64-linux-gnu/7 -L/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/7/../../../../lib -L/lib/x86_64-linux-gnu -L/lib/../lib -L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-linux-gnu/7/../../.. hello.o -lgcc --push-state --as-needed -lgcc_s --pop-state -lc -lgcc --push-state --as-needed -lgcc_s --pop-state /usr/lib/gcc/x86_64-linux-gnu/7/crtendS.o /usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/crtn.o</span><br><span class="line">COLLECT_GCC_OPTIONS=&apos;-v&apos; &apos;-save-temps&apos; &apos;-mtune=generic&apos; &apos;-march=x86-64&apos;</span><br></pre></td></tr></table></figure><p><strong>注意</strong>:在链接时使用的是<code>collect2</code>,不是<code>ld</code>,那么二者有什么关系??</p><blockquote><p><code>collect2</code>是<code>ld</code>链接器的一个<code>封装</code>,最终还是要调用ld来完成链接工作,collect2的作用是在实现main函数的代码前对目标文件中命名的特殊符号进行收集. 这些特殊的符号表明它们是全局构造函数或在main前执行，collect2会生成一个临时的.c文件，将这些符号的地址收集成一个数组，然后放到这个.c文件里面，编译后与其他目标文件一起被链接到最终的输出文件中。在这里我们没有加-nostdlib,所以自然不会调用__main,也就不会链接main函数所需引用的目标文件,也就不会对那些特殊的符号进行收集.</p></blockquote><h2 id="目标文件–Object-File"><a href="#目标文件–Object-File" class="headerlink" title="目标文件–Object File"></a>目标文件–Object File</h2><p>目标文件及可执行文件,主要格式Windows下<code>PE(Portable Executable)</code>和Linux的<code>ELF(Executable Linkable Format)</code>,他们都是<code>COFF(Common file format)</code>的变种</p><blockquote><p>可执行文件按照可执行文件格式存储,<code>动态链接库(DLL, Dynamic Linking Library)</code>及<code>静态链接库(Static Linking Library)</code>文件都是按照可执行文件格式存储</p></blockquote><h3 id="文件格式-ELF"><a href="#文件格式-ELF" class="headerlink" title="文件格式-ELF"></a>文件格式-ELF</h3><ul><li>ELF格式文件分类:</li></ul><table><thead><tr><th style="text-align:center">ELF文件类型</th><th style="text-align:center">说明</th><th style="text-align:center">示例</th></tr></thead><tbody><tr><td style="text-align:center">可重定位文件(Relocatablr File)</td><td style="text-align:center">包含代码和数据,可以被用来链接成可执行文件或共享目标文件,<code>静态链接库属于这类</code></td><td style="text-align:center">Linux的.o, Windows的.obj</td></tr><tr><td style="text-align:center">可执行文件(Executable File)</td><td style="text-align:center">包含可以直接执行的程序,它的代表就是ELF可执行文件,一般没有扩展名</td><td style="text-align:center">比如/bin/bash文件, Windows的.exe</td></tr><tr><td style="text-align:center">共享目标文件(Share Object File)</td><td style="text-align:center">包含代码和数据,可以在两种情况下使用,一种链接器使用生成目标文件,另一种动态链接器可以将几个共享目标文件和可执行文件结合,作为进程映像一起运行</td><td style="text-align:center">Linux的.so, Windows的DLL</td></tr><tr><td style="text-align:center">核心转储文件(Core Dump File)</td><td style="text-align:center">当进程意外终止时,系统可以将该进程的地址空间内容及终止时的一些其他信息转存到核心转储文件</td><td style="text-align:center">Linux下的core dump</td></tr></tbody></table><ul><li>查看</li></ul><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">=====&gt;$file a.out</span><br><span class="line">a.out: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=fadad58a4fd9204e015da490f57908c27ba46ccf, not stripped</span><br></pre></td></tr></table></figure><h3 id="ELF的格式"><a href="#ELF的格式" class="headerlink" title="ELF的格式"></a>ELF的格式</h3><p>目标文件的内容包含代码和数据,还有链接时必要的一些信息,比如符号表,调试信息,字符串等.一般目标文件将这些信息按不同的属性,以<code>&quot;节&quot;(Section)</code>的形式存储,有时候也就<code>&quot;段&quot;(Segment)</code>,在一般情况下都表示一个一定长度的区域</p><blockquote><p><code>Section</code>与<code>Segment</code>的区别:???</p><ul><li>在ELF的<code>链接视图</code>(编译)中,不同的属性称为<code>Section</code></li><li>在ELF的<code>装载视图</code>(运行)中,不同的属性称为<code>Segment</code></li></ul></blockquote><p>ELF文件:</p><table><thead><tr><th style="text-align:center">Executable File / Object File</th><th style="text-align:left">说明</th></tr></thead><tbody><tr><td style="text-align:center">File Header</td><td style="text-align:left">包含一个<code>段表(Section Table)</code></td></tr><tr><td style="text-align:center">.text section</td><td style="text-align:left">代码段, 机器指令</td></tr><tr><td style="text-align:center">.data section</td><td style="text-align:left">数据段, 全局变量和局部静态变量数据</td></tr><tr><td style="text-align:center">.bss section</td><td style="text-align:left">未初始化的全局变量和局部静态变量,默认值为<code>0</code></td></tr></tbody></table><p><strong>注</strong>:</p><ul><li><code>段表</code>,是一个描述文件中各个段的数组,描述了文件中各个段在文件中的偏移位置及段的属性等</li><li><code>.bss</code>, 只是为未初始化的全局变量和局部静态变量预留位置而已,它并没有内容,所以它在文件中也不占空间</li></ul><p><strong>总体来说,程序源代码被编译后主要分成两种段, <code>程序指令</code>和<code>程序数据</code>,代码段属于程序指令,而数据段和.bss段 属于程序数据</strong></p><h3 id="数据和指令分段的好处"><a href="#数据和指令分段的好处" class="headerlink" title="数据和指令分段的好处"></a>数据和指令分段的好处</h3><ol><li><code>权限控制</code>, 当程序被装载后,数据和指令分别被映射到两个虚存区域.由于数据局域对进程来说是<code>可读写</code>的,指令区域对进程来说是<code>只读</code>的,所以这两个虚存区域的权限可以被分别设置成可读写和只读.这样可以<strong>防止程序被有意或无意的修改.</strong></li><li>CPU的设计角度出发,现代CPU都有着缓存(Cache)体系.指令区和数据区的分离有利于提高程序的局部性.现代CPU的缓存一般都设计成<strong>数据缓存(D-Cache)和指令缓存(I-Cache)分离,所以程序的指令和数据分开存放对CPU的缓存命中率提高有好处.</strong></li><li><code>指令共享</code>, 当系统中运行多个该程序的副本时,它们的指令都是一样的,所以内存中只需要保存一份程序的指令部分.</li></ol><h2 id="挖掘SimpleSection-o"><a href="#挖掘SimpleSection-o" class="headerlink" title="挖掘SimpleSection.o"></a>挖掘SimpleSection.o</h2><p>示例分析ELF格式的文件</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/*#############################################################</span></span><br><span class="line"><span class="comment"> *     File Name	: SimpleSection.c</span></span><br><span class="line"><span class="comment"> *     Author		: winddoing</span></span><br><span class="line"><span class="comment"> *     Created Time	: 2018年12月18日 星期二 15时17分55秒</span></span><br><span class="line"><span class="comment"> *     Description	:</span></span><br><span class="line"><span class="comment"> *          gcc -c SimpleSection.c -o SimpleSection.o</span></span><br><span class="line"><span class="comment"> *          gcc version 7.3.0 (Ubuntu 7.3.0-27ubuntu1~18.04)</span></span><br><span class="line"><span class="comment"> *############################################################*/</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">printf</span><span class="params">(<span class="keyword">const</span> <span class="keyword">char</span>* format, ...)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">int</span> global_init_var = <span class="number">84</span>;</span><br><span class="line"><span class="keyword">int</span> global_uninit_var;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">func1</span><span class="params">(<span class="keyword">int</span> i)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">"%d\n"</span>, i);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">int</span> argc, <span class="keyword">const</span> <span class="keyword">char</span> *argv[])</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">int</span> static_var = <span class="number">85</span>;</span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">int</span> static_var2;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">int</span> a = <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">int</span> b;</span><br><span class="line"></span><br><span class="line">    func1(static_var + static_var2 + a + b);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> a;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>系统环境: ubuntu18.04 64bit, gcc version 7.3.0</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">gcc -c SimpleSection.c -o SimpleSection.o</span><br></pre></td></tr></table></figure><blockquote><p><code>-c</code>: 表示只编译不链接</p></blockquote><p>Object文件的内部结构:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$objdump -h SimpleSection.o</span><br></pre></td></tr></table></figure><blockquote><ul><li><code>-h</code>: 打印ELF文件各个段的基本信息</li><li><code>-x</code>: 全部详细打印输出</li></ul></blockquote><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">SimpleSection.o:     file format elf64-x86<span class="number">-64</span></span><br><span class="line"></span><br><span class="line">Sections:</span><br><span class="line">Idx Name          Size      VMA               LMA               <span class="built_in">File</span> off  Algn</span><br><span class="line">  <span class="number">0</span> .<span class="built_in">text</span>         <span class="number">0000005</span>e  <span class="number">0000000000000000</span>  <span class="number">0000000000000000</span>  <span class="number">00000040</span>  <span class="number">2</span>**<span class="number">0</span></span><br><span class="line">                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE</span><br><span class="line">  <span class="number">1</span> .data         <span class="number">00000008</span>  <span class="number">0000000000000000</span>  <span class="number">0000000000000000</span>  <span class="number">000000</span>a0  <span class="number">2</span>**<span class="number">2</span></span><br><span class="line">                  CONTENTS, ALLOC, LOAD, DATA</span><br><span class="line">  <span class="number">2</span> .bss          <span class="number">00000004</span>  <span class="number">0000000000000000</span>  <span class="number">0000000000000000</span>  <span class="number">000000</span>a8  <span class="number">2</span>**<span class="number">2</span></span><br><span class="line">                  ALLOC</span><br><span class="line">  <span class="number">3</span> .rodata       <span class="number">00000004</span>  <span class="number">0000000000000000</span>  <span class="number">0000000000000000</span>  <span class="number">000000</span>a8  <span class="number">2</span>**<span class="number">0</span></span><br><span class="line">                  CONTENTS, ALLOC, LOAD, READONLY, DATA</span><br><span class="line">  <span class="number">4</span> .comment      <span class="number">0000002b</span>  <span class="number">0000000000000000</span>  <span class="number">0000000000000000</span>  <span class="number">000000</span>ac  <span class="number">2</span>**<span class="number">0</span></span><br><span class="line">                  CONTENTS, READONLY</span><br><span class="line">  <span class="number">5</span> .note.GNU-<span class="built_in">stack</span> <span class="number">00000000</span>  <span class="number">0000000000000000</span>  <span class="number">0000000000000000</span>  <span class="number">000000</span>d7  <span class="number">2</span>**<span class="number">0</span></span><br><span class="line">                  CONTENTS, READONLY</span><br><span class="line">  <span class="number">6</span> .eh_frame     <span class="number">00000058</span>  <span class="number">0000000000000000</span>  <span class="number">0000000000000000</span>  <span class="number">000000</span>d8  <span class="number">2</span>**<span class="number">3</span></span><br><span class="line">                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, DATA</span><br></pre></td></tr></table></figure><table><thead><tr><th style="text-align:center">Section Name</th><th style="text-align:center">注释</th></tr></thead><tbody><tr><td style="text-align:center">.text</td><td style="text-align:center">代码段</td></tr><tr><td style="text-align:center">.data</td><td style="text-align:center">数据段</td></tr><tr><td style="text-align:center">.bss</td><td style="text-align:center">BSS段</td></tr><tr><td style="text-align:center">.rodata</td><td style="text-align:center">只读数据段</td></tr><tr><td style="text-align:center">.comment</td><td style="text-align:center">注释信息段</td></tr><tr><td style="text-align:center">.note.GNU-stack</td><td style="text-align:center">堆栈提示段</td></tr><tr><td style="text-align:center">.eh_frame</td><td style="text-align:center">展开堆栈所需的调用帧信息</td></tr></tbody></table><blockquote><p>eh_frame, <a href="http://blog.51cto.com/zorro/1034925" target="_blank" rel="noopener">http://blog.51cto.com/zorro/1034925</a></p></blockquote><h3 id="ELF的结构"><a href="#ELF的结构" class="headerlink" title="ELF的结构"></a>ELF的结构</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line">+----------------------+</span><br><span class="line">|                      |</span><br><span class="line">|                      |</span><br><span class="line">|                      |</span><br><span class="line">|      Other Data      |</span><br><span class="line">|                      |</span><br><span class="line">|                      |</span><br><span class="line">|                      |</span><br><span class="line">|                      |</span><br><span class="line">+----------------------+ 0x0000 00d8</span><br><span class="line">|       .eh_frame      |</span><br><span class="line">+----------------------+ 0x0000 00d7</span><br><span class="line">|                      |</span><br><span class="line">|       .comment       |</span><br><span class="line">|                      |</span><br><span class="line">+----------------------+ 0x0000 00ac</span><br><span class="line">|       .rodata        |</span><br><span class="line">|                      |</span><br><span class="line">+----------------------+ 0x0000 00a8</span><br><span class="line">|        .data         |</span><br><span class="line">+----------------------+ 0x0000 00a0</span><br><span class="line">|                      |</span><br><span class="line">|                      |</span><br><span class="line">|        .text         |</span><br><span class="line">|                      |</span><br><span class="line">|                      |</span><br><span class="line">+----------------------+ 0x0000 0040</span><br><span class="line">|                      |</span><br><span class="line">|     ELF Header       |</span><br><span class="line">|                      |</span><br><span class="line">+----------------------+ 0x0000 0000</span><br></pre></td></tr></table></figure><p>查看ELF文件中代码段,数据段,和BSS段的长度,<code>size</code>命令</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">$size SimpleSection.o</span><br><span class="line">   text	   data	    bss	    dec	    hex	filename</span><br><span class="line">    186	      8	      4	    198	     c6	SimpleSection.o</span><br></pre></td></tr></table></figure><h3 id="代码段"><a href="#代码段" class="headerlink" title="代码段"></a>代码段</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$objdump -s -d SimpleSection.o</span><br></pre></td></tr></table></figure><blockquote><ul><li><code>-s</code>: 将所有段的内容以十六进制的方式打印出来</li><li><code>-d</code>: 将所有包含指令的段反汇编</li></ul></blockquote><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br></pre></td><td class="code"><pre><span class="line">SimpleSection.o:     file format elf64-x86<span class="number">-64</span></span><br><span class="line"></span><br><span class="line">Contents of section .<span class="built_in">text</span>:</span><br><span class="line"> <span class="number">0000</span> <span class="number">554889e5</span> <span class="number">4883</span>ec10 <span class="number">897</span>dfc8b <span class="number">45f</span>c89c6  UH..H....&#125;..E...</span><br><span class="line"> <span class="number">0010</span> <span class="number">488</span>d3d00 <span class="number">000000b</span>8 <span class="number">00000000</span> e8000000  H.=.............</span><br><span class="line"> <span class="number">0020</span> <span class="number">0090</span>c9c3 <span class="number">554889e5</span> <span class="number">4883</span>ec20 <span class="number">897</span>dec48  ....UH..H.. .&#125;.H</span><br><span class="line"> <span class="number">0030</span> <span class="number">8975e0</span>c7 <span class="number">45f</span>80100 <span class="number">00008b</span>15 <span class="number">00000000</span>  .u..E...........</span><br><span class="line"> <span class="number">0040</span> <span class="number">8b</span>050000 <span class="number">000001</span>c2 <span class="number">8b</span>45f801 c28b45fc  .........E....E.</span><br><span class="line"> <span class="number">0050</span> <span class="number">01</span>d089c7 e8000000 <span class="number">008b</span>45f8 c9c3      ..........E...</span><br><span class="line">Contents of section .data:</span><br><span class="line"> <span class="number">0000</span> <span class="number">54000000</span> <span class="number">55000000</span>                    T...U...</span><br><span class="line">Contents of section .rodata:</span><br><span class="line"> <span class="number">0000</span> <span class="number">25640</span>a00                             %d..</span><br><span class="line">Contents of section .comment:</span><br><span class="line"> <span class="number">0000</span> <span class="number">00474343</span> <span class="number">3</span>a202855 <span class="number">62756e74</span> <span class="number">7520372</span>e  .GCC: (Ubuntu <span class="number">7.</span></span><br><span class="line"> <span class="number">0010</span> <span class="number">332e302</span>d <span class="number">32377562</span> <span class="number">756e7475</span> <span class="number">317e3138</span>  <span class="number">3.0</span><span class="number">-27u</span>buntu1~<span class="number">18</span></span><br><span class="line"> <span class="number">0020</span> <span class="number">2e303429</span> <span class="number">20372e33</span> <span class="number">2e3000</span>             <span class="number">.04</span>) <span class="number">7.3</span><span class="number">.0</span>.</span><br><span class="line">Contents of section .eh_frame:</span><br><span class="line"> <span class="number">0000</span> <span class="number">14000000</span> <span class="number">00000000</span> <span class="number">017</span>a5200 <span class="number">01781001</span>  .........zR..x..</span><br><span class="line"> <span class="number">0010</span> <span class="number">1b</span>0c0708 <span class="number">90010000</span> <span class="number">1</span>c000000 <span class="number">1</span>c000000  ................</span><br><span class="line"> <span class="number">0020</span> <span class="number">00000000</span> <span class="number">24000000</span> <span class="number">00410e10</span> <span class="number">8602430</span>d  ....$....A....C.</span><br><span class="line"> <span class="number">0030</span> <span class="number">065f</span>0c07 <span class="number">08000000</span> <span class="number">1</span>c000000 <span class="number">3</span>c000000  ._..........&lt;...</span><br><span class="line"> <span class="number">0040</span> <span class="number">00000000</span> <span class="number">3</span>a000000 <span class="number">00410e10</span> <span class="number">8602430</span>d  ....:....A....C.</span><br><span class="line"> <span class="number">0050</span> <span class="number">06750</span>c07 <span class="number">08000000</span>                    .u......</span><br><span class="line"></span><br><span class="line">Disassembly of section .<span class="built_in">text</span>:</span><br><span class="line"></span><br><span class="line"><span class="number">0000000000000000</span> &lt;func1&gt;:</span><br><span class="line">   <span class="number">0</span>:	<span class="number">55</span>                   	push   %rbp</span><br><span class="line">   <span class="number">1</span>:	<span class="number">48</span> <span class="number">89</span> e5             	mov    %rsp,%rbp</span><br><span class="line">   <span class="number">4</span>:	<span class="number">48</span> <span class="number">83</span> ec <span class="number">10</span>          	sub    $<span class="number">0x10</span>,%rsp</span><br><span class="line">   <span class="number">8</span>:	<span class="number">89</span> <span class="number">7</span>d fc             	mov    %edi,<span class="number">-0x4</span>(%rbp)</span><br><span class="line">   b:	<span class="number">8b</span> <span class="number">45</span> fc             	mov    <span class="number">-0x4</span>(%rbp),%eax</span><br><span class="line">   e:	<span class="number">89</span> c6                	mov    %eax,%esi</span><br><span class="line">  <span class="number">10</span>:	<span class="number">48</span> <span class="number">8</span>d <span class="number">3</span>d <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> 	lea    <span class="number">0x0</span>(%rip),%rdi        # <span class="number">17</span> &lt;func1+<span class="number">0x17</span>&gt;</span><br><span class="line">  <span class="number">17</span>:	b8 <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span>       	mov    $<span class="number">0x0</span>,%eax</span><br><span class="line">  <span class="number">1</span>c:	e8 <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span>       	callq  <span class="number">21</span> &lt;func1+<span class="number">0x21</span>&gt;</span><br><span class="line">  <span class="number">21</span>:	<span class="number">90</span>                   	nop</span><br><span class="line">  <span class="number">22</span>:	c9                   	leaveq</span><br><span class="line">  <span class="number">23</span>:	c3                   	retq</span><br><span class="line"></span><br><span class="line"><span class="number">0000000000000024</span> &lt;main&gt;:</span><br><span class="line">  <span class="number">24</span>:	<span class="number">55</span>                   	push   %rbp</span><br><span class="line">  <span class="number">25</span>:	<span class="number">48</span> <span class="number">89</span> e5             	mov    %rsp,%rbp</span><br><span class="line">  <span class="number">28</span>:	<span class="number">48</span> <span class="number">83</span> ec <span class="number">20</span>          	sub    $<span class="number">0x20</span>,%rsp</span><br><span class="line">  <span class="number">2</span>c:	<span class="number">89</span> <span class="number">7</span>d ec             	mov    %edi,<span class="number">-0x14</span>(%rbp)</span><br><span class="line">  <span class="number">2f</span>:	<span class="number">48</span> <span class="number">89</span> <span class="number">75</span> e0          	mov    %rsi,<span class="number">-0x20</span>(%rbp)</span><br><span class="line">  <span class="number">33</span>:	c7 <span class="number">45</span> f8 <span class="number">01</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> 	movl   $<span class="number">0x1</span>,<span class="number">-0x8</span>(%rbp)</span><br><span class="line">  <span class="number">3</span>a:	<span class="number">8b</span> <span class="number">15</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span>    	mov    <span class="number">0x0</span>(%rip),%edx        # <span class="number">40</span> &lt;main+<span class="number">0x1c</span>&gt;</span><br><span class="line">  <span class="number">40</span>:	<span class="number">8b</span> <span class="number">05</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span>    	mov    <span class="number">0x0</span>(%rip),%eax        # <span class="number">46</span> &lt;main+<span class="number">0x22</span>&gt;</span><br><span class="line">  <span class="number">46</span>:	<span class="number">01</span> c2                	add    %eax,%edx</span><br><span class="line">  <span class="number">48</span>:	<span class="number">8b</span> <span class="number">45</span> f8             	mov    <span class="number">-0x8</span>(%rbp),%eax</span><br><span class="line">  <span class="number">4b</span>:	<span class="number">01</span> c2                	add    %eax,%edx</span><br><span class="line">  <span class="number">4</span>d:	<span class="number">8b</span> <span class="number">45</span> fc             	mov    <span class="number">-0x4</span>(%rbp),%eax</span><br><span class="line">  <span class="number">50</span>:	<span class="number">01</span> d0                	add    %edx,%eax</span><br><span class="line">  <span class="number">52</span>:	<span class="number">89</span> c7                	mov    %eax,%edi</span><br><span class="line">  <span class="number">54</span>:	e8 <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span>       	callq  <span class="number">59</span> &lt;main+<span class="number">0x35</span>&gt;</span><br><span class="line">  <span class="number">59</span>:	<span class="number">8b</span> <span class="number">45</span> f8             	mov    <span class="number">-0x8</span>(%rbp),%eax</span><br><span class="line">  <span class="number">5</span>c:	c9                   	leaveq</span><br><span class="line">  <span class="number">5</span>d:	c3                   	retq</span><br></pre></td></tr></table></figure><h3 id="数据段和只读数据段"><a href="#数据段和只读数据段" class="headerlink" title="数据段和只读数据段"></a>数据段和只读数据段</h3><ul><li><p>数据段: <code>.data</code>主要存放<code>初始化</code>了的<strong>全局静态变量</strong>和<strong>局部静态变量</strong></p><blockquote><p>在SimpleSection.c示例中, 有这样两个变量<code>global_init_var</code>和<code>static_var</code>, 一共8字节,所以<code>.data</code>段的大小将是8字节</p></blockquote></li><li><p>只读数据段: <code>.rodata</code>存放的只读数据.一般是程序中的<strong>只读变量</strong>(const修饰的变量)和<strong>字符串常量</strong></p><blockquote><p><strong>好处:</strong></p><ul><li>操作系统在加载时,将<code>.rodata</code>段映射成只读后,任何对这个段的操作,将被视为非法操作,保证了程序的安全性</li><li>在嵌入式平台中,有些存储器是采用的只读存储器,如ROM,这样将<code>rodata</code>段放在该存储区域就可以保证程序访问存储器的正确性(比如固化在CPU中的bootram的数据段映射)</li></ul></blockquote></li></ul><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">$</span><span class="bash">objdump -s SimpleSection.o</span></span><br></pre></td></tr></table></figure><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">Contents of section .data:</span><br><span class="line"> <span class="number">0000</span> <span class="number">54000000</span> <span class="number">55000000</span>                    T...U...</span><br><span class="line">Contents of section .rodata:</span><br><span class="line"> <span class="number">0000</span> <span class="number">25640</span>a00                             %d..</span><br></pre></td></tr></table></figure><p><code>global_init_var=84</code>十六进制表示<code>54</code>,占四字节,由于是<strong>小端模式Little Endian</strong>, 排列顺序:[54 00 00 00]</p><h3 id="BBS段"><a href="#BBS段" class="headerlink" title="BBS段"></a>BBS段</h3><ul><li>BBS段: <code>.bss</code>存放<code>未初始化</code>的<strong>全局变量</strong>和<strong>局部静态变量</strong><blockquote><p>在示例中<code>global_uninit_var</code>和<code>static_var2</code>两个变量将存放在BSS段,准确说就是.bss段为其预留空间,但是我们看到该段大小只有4字节,而这两个变量的大小是8字节.</p></blockquote></li></ul><p>不同的语言与不同的编译器实现有关,有些编译器会将<strong>全局的未初始化变量</strong>存放到BSS段,有些则不存放,只是预留一个<strong>未定义的全局变量符号</strong>,等到最终链接成可执行文件时,再在BSS段分配空间.(弱符号和强符号)</p><p><strong>编译单元内部可见的静态变量</strong>(static修饰),的确存放在BSS段</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">$</span><span class="bash">objdump -h SimpleSection.o</span></span><br></pre></td></tr></table></figure><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">Sections:</span><br><span class="line">Idx Name          Size      VMA               LMA               <span class="built_in">File</span> off  Algn</span><br><span class="line">  <span class="number">2</span> .bss          <span class="number">00000004</span>  <span class="number">0000000000000000</span>  <span class="number">0000000000000000</span>  <span class="number">000000</span>a8  <span class="number">2</span>**<span class="number">2</span></span><br><span class="line">                  ALLOC</span><br></pre></td></tr></table></figure><h3 id="其他段"><a href="#其他段" class="headerlink" title="其他段"></a>其他段</h3><table><thead><tr><th style="text-align:center">常用段名</th><th style="text-align:left">说明</th></tr></thead><tbody><tr><td style="text-align:center"><code>.rodata1</code></td><td style="text-align:left">Read only Data 只存放只读数据,比如字符串常量,全局const变量,和<code>.rodata</code>一样</td></tr><tr><td style="text-align:center"><code>.comment</code></td><td style="text-align:left">存放编译器版本信息,比如字符串:”.GCC: (Ubuntu 7.3.0-27ubuntu1~18.04) 7.3.0.”</td></tr><tr><td style="text-align:center"><code>.debug</code></td><td style="text-align:left">调试信息</td></tr><tr><td style="text-align:center"><code>.dynamic</code></td><td style="text-align:left">动态链接信息</td></tr><tr><td style="text-align:center"><code>.hash</code></td><td style="text-align:left">符号哈希表</td></tr><tr><td style="text-align:center"><code>.line</code></td><td style="text-align:left">调试时的行号表,即源代码行号与编译后指令的对应表</td></tr><tr><td style="text-align:center"><code>.note</code></td><td style="text-align:left">额外的编译器信息,比如程序的公司名,发布版本号</td></tr><tr><td style="text-align:center"><code>.strtab</code></td><td style="text-align:left">String Table字符串表,用于存储ELF文件中用到的各种字符串</td></tr><tr><td style="text-align:center"><code>.symtab</code></td><td style="text-align:left">Symbol Table符号表</td></tr><tr><td style="text-align:center"><code>.shstrtab</code></td><td style="text-align:left">Section String Table 段名表</td></tr><tr><td style="text-align:center"><code>.plt</code> <code>.got</code></td><td style="text-align:left">动态链接的跳转表和全局入口表</td></tr><tr><td style="text-align:center"><code>.init</code> <code>fini</code></td><td style="text-align:left">程序初始化和终结代码段</td></tr></tbody></table><ul><li>将一个二进制文件,比如图片,音乐作为目标文件中的一个段, 使用<code>objcopy</code>工具</li></ul><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">$</span><span class="bash">objcopy -I binary -O elf64-x86-64  pic.jpg pic.o</span></span><br></pre></td></tr></table></figure><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">$</span><span class="bash">objdump -ht pic.o</span></span><br></pre></td></tr></table></figure><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">pic.o:     file format elf64-little</span><br><span class="line"></span><br><span class="line">Sections:</span><br><span class="line">Idx Name          Size      VMA               LMA               <span class="built_in">File</span> off  Algn</span><br><span class="line">  <span class="number">0</span> .data         <span class="number">000799</span>df  <span class="number">0000000000000000</span>  <span class="number">0000000000000000</span>  <span class="number">00000040</span>  <span class="number">2</span>**<span class="number">0</span></span><br><span class="line">                  CONTENTS, ALLOC, LOAD, DATA</span><br><span class="line">SYMBOL TABLE:</span><br><span class="line"><span class="number">0000000000000000</span> l    d  .data	<span class="number">0000000000000000</span> .data</span><br><span class="line"><span class="number">0000000000000000</span> g       .data	<span class="number">0000000000000000</span> _binary_pic_jpg_start</span><br><span class="line"><span class="number">00000000000799</span>df g       .data	<span class="number">0000000000000000</span> _binary_pic_jpg_end</span><br><span class="line"><span class="number">00000000000799</span>df g       *ABS*	<span class="number">0000000000000000</span> _binary_pic_jpg_size</span><br></pre></td></tr></table></figure><p>符号<code>_binary_pic_jpg_start</code>,<code>_binary_pic_jpg_end</code>,<code>_binary_pic_jpg_size</code>表示该图片文件所在内存中的起始地址,结束地址和大小.可以在程序中直接声明并使用它们.</p><h3 id="自定义段"><a href="#自定义段" class="headerlink" title="自定义段"></a>自定义段</h3><p>GCC提供的一种扩展机制,可以指定变量所处的段.</p><blockquote><p>比如为了满足某些硬件的内存或IO地址布局,将某些变量或代码放到指定的段</p></blockquote><p>在全局变量或函数前加<code>&quot;__attribute__((section(&quot;name&quot;)))&quot;</code>属性就可以把相应的变量或函数放到以<code>&quot;name&quot;</code>作为段名的段中<br></p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">__attribute__((section(<span class="string">"FOO"</span>))) <span class="keyword">int</span> global = <span class="number">42</span>;</span><br><span class="line">__attribute__((section(<span class="string">"BAR"</span>))) <span class="function"><span class="keyword">void</span> <span class="title">foo</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><h2 id="ELF文件结构描述"><a href="#ELF文件结构描述" class="headerlink" title="ELF文件结构描述"></a>ELF文件结构描述</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">+----------------------+</span><br><span class="line">|     ELF Header       |</span><br><span class="line">+----------------------+</span><br><span class="line">|        .text         |</span><br><span class="line">+----------------------+</span><br><span class="line">|        .data         |</span><br><span class="line">+----------------------+</span><br><span class="line">|        .bss          |</span><br><span class="line">+----------------------+</span><br><span class="line">|        ...           |</span><br><span class="line">+----------------------+</span><br><span class="line">|    other sections    |</span><br><span class="line">+----------------------+</span><br><span class="line">| Section header table | &lt;== 段表: 描述ELF文件所有段的信息</span><br><span class="line">+----------------------+</span><br><span class="line">|   String Tables      |</span><br><span class="line">|                      |</span><br><span class="line">|   Symbol Tables      |</span><br><span class="line">|                      |</span><br><span class="line">|                      |</span><br><span class="line">+----------------------+</span><br></pre></td></tr></table></figure><blockquote><p>ELF文件分析工具: <code>readelf</code></p></blockquote><h3 id="头文件"><a href="#头文件" class="headerlink" title="头文件"></a>头文件</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">$</span><span class="bash">readelf -h SimpleSection.o</span></span><br></pre></td></tr></table></figure><blockquote><ul><li><code>-h</code>: Display the ELF file header</li></ul></blockquote><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">头            ELF Header:</span><br><span class="line">ELF魔数          Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00</span><br><span class="line">文件机器字节长度   Class:                             ELF64</span><br><span class="line">数据存储方式      Data:                              2&apos;s complement, little endian</span><br><span class="line">版本            Version:                           1 (current)</span><br><span class="line">运行平台         OS/ABI:                            UNIX - System V</span><br><span class="line">ABI版本         ABI Version:                       0</span><br><span class="line">ELF重定位类型    Type:                              REL (Relocatable file)</span><br><span class="line">硬件平台         Machine:                           Advanced Micro Devices X86-64</span><br><span class="line">硬件平台版本      Version:                           0x1</span><br><span class="line">入口地址         Entry point address:               0x0</span><br><span class="line">程序入口         Start of program headers:          0 (bytes into file)</span><br><span class="line">段表位置         Start of section headers:          1112 (bytes into file)</span><br><span class="line">                Flags:                             0x0</span><br><span class="line">                Size of this header:               64 (bytes)</span><br><span class="line">                Size of program headers:           0 (bytes)</span><br><span class="line">                Number of program headers:         0</span><br><span class="line">                Size of section headers:           64 (bytes)</span><br><span class="line">                Number of section headers:         13</span><br><span class="line">                Section header string table index: 12</span><br></pre></td></tr></table></figure><ul><li>数据结构定义</li></ul><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">  <span class="keyword">unsigned</span> <span class="keyword">char</span> e_ident[EI_NIDENT]; <span class="comment">/* Magic number and other info */</span></span><br><span class="line">  Elf64_Half    e_type;         <span class="comment">/* Object file type */</span></span><br><span class="line">  Elf64_Half    e_machine;      <span class="comment">/* Architecture */</span></span><br><span class="line">  Elf64_Word    e_version;      <span class="comment">/* Object file version */</span></span><br><span class="line">  Elf64_Addr    e_entry;        <span class="comment">/* Entry point virtual address */</span></span><br><span class="line">  Elf64_Off e_phoff;        <span class="comment">/* Program header table file offset */</span></span><br><span class="line">  Elf64_Off e_shoff;        <span class="comment">/* Section header table file offset */</span></span><br><span class="line">  Elf64_Word    e_flags;        <span class="comment">/* Processor-specific flags */</span></span><br><span class="line">  Elf64_Half    e_ehsize;       <span class="comment">/* ELF header size in bytes */</span></span><br><span class="line">  Elf64_Half    e_phentsize;        <span class="comment">/* Program header table entry size */</span></span><br><span class="line">  Elf64_Half    e_phnum;        <span class="comment">/* Program header table entry count */</span></span><br><span class="line">  Elf64_Half    e_shentsize;        <span class="comment">/* Section header table entry size */</span></span><br><span class="line">  Elf64_Half    e_shnum;        <span class="comment">/* Section header table entry count */</span></span><br><span class="line">  Elf64_Half    e_shstrndx;     <span class="comment">/* Section header string table index */</span></span><br><span class="line">&#125; Elf64_Ehdr;</span><br></pre></td></tr></table></figure><blockquote><p>file: /usr/include/elf.h</p></blockquote><blockquote><p><strong>ELF魔数</strong>: 最开始的4个字节所有的ELF文件 都必须相同,分别是<code>0x7f</code>,<code>0x45</code>,<code>0x4c</code>, <code>0x46</code></p><ul><li>第一个字节对应ASCII字符: DEL字符</li><li>后面3个字符对应ASCII字符: E, L, F</li></ul></blockquote><h3 id="段表"><a href="#段表" class="headerlink" title="段表"></a>段表</h3><p><strong>段表</strong>:保存各个段的基本属性,描述各个段的信息,如段名,段的长度,在文件中的偏移,读写权限等</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">$</span><span class="bash">readelf -S SimpleSection.o</span></span><br></pre></td></tr></table></figure><blockquote><ul><li><code>-S</code>: Display the sections’ header,每个段的头信息</li></ul></blockquote><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line">There are 13 section headers, starting at offset 0x458:</span><br><span class="line"></span><br><span class="line">Section Headers:</span><br><span class="line">  [Nr] Name              Type             Address           Offset</span><br><span class="line">       Size              EntSize          Flags  Link  Info  Align</span><br><span class="line">  [ 0]                   NULL             0000000000000000  00000000</span><br><span class="line">       0000000000000000  0000000000000000           0     0     0</span><br><span class="line">  [ 1] .text             PROGBITS         0000000000000000  00000040</span><br><span class="line">       000000000000005e  0000000000000000  AX       0     0     1</span><br><span class="line">  [ 2] .rela.text        RELA             0000000000000000  00000348</span><br><span class="line">       0000000000000078  0000000000000018   I      10     1     8</span><br><span class="line">  [ 3] .data             PROGBITS         0000000000000000  000000a0</span><br><span class="line">       0000000000000008  0000000000000000  WA       0     0     4</span><br><span class="line">  [ 4] .bss              NOBITS           0000000000000000  000000a8</span><br><span class="line">       0000000000000004  0000000000000000  WA       0     0     4</span><br><span class="line">  [ 5] .rodata           PROGBITS         0000000000000000  000000a8</span><br><span class="line">       0000000000000004  0000000000000000   A       0     0     1</span><br><span class="line">  [ 6] .comment          PROGBITS         0000000000000000  000000ac</span><br><span class="line">       000000000000002b  0000000000000001  MS       0     0     1</span><br><span class="line">  [ 7] .note.GNU-stack   PROGBITS         0000000000000000  000000d7</span><br><span class="line">       0000000000000000  0000000000000000           0     0     1</span><br><span class="line">  [ 8] .eh_frame         PROGBITS         0000000000000000  000000d8</span><br><span class="line">       0000000000000058  0000000000000000   A       0     0     8</span><br><span class="line">  [ 9] .rela.eh_frame    RELA             0000000000000000  000003c0</span><br><span class="line">       0000000000000030  0000000000000018   I      10     8     8</span><br><span class="line">  [10] .symtab           SYMTAB           0000000000000000  00000130</span><br><span class="line">       0000000000000198  0000000000000018          11    11     8</span><br><span class="line">  [11] .strtab           STRTAB           0000000000000000  000002c8</span><br><span class="line">       000000000000007c  0000000000000000           0     0     1</span><br><span class="line">  [12] .shstrtab         STRTAB           0000000000000000  000003f0</span><br><span class="line">       0000000000000061  0000000000000000           0     0     1</span><br><span class="line">Key to Flags:</span><br><span class="line">  W (write), A (alloc), X (execute), M (merge), S (strings), I (info),</span><br><span class="line">  L (link order), O (extra OS processing required), G (group), T (TLS),</span><br><span class="line">  C (compressed), x (unknown), o (OS specific), E (exclude),</span><br><span class="line">  l (large), p (processor specific)</span><br></pre></td></tr></table></figure><ul><li>各段在文件的分布:<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br></pre></td><td class="code"><pre><span class="line">start +------------------+0x0000 0000</span><br><span class="line">      |   ELF Header     |</span><br><span class="line">      |   e_shoff=0x458 +--------------------+</span><br><span class="line">      +------------------+0x0000 0040        |</span><br><span class="line">      |                  |                   |</span><br><span class="line">      |     .text        |                   |</span><br><span class="line">      |                  |                   |</span><br><span class="line">      +------------------+0x0000 00a0        |</span><br><span class="line">      |     .data        |                   |</span><br><span class="line">      +------------------+0x0000 00a8        |</span><br><span class="line">      |     .rodata      |                   |</span><br><span class="line">      +------------------+0x0000 00ac        |</span><br><span class="line">      |                  |                   |</span><br><span class="line">      |     .comment     |                   |</span><br><span class="line">      +------------------+0x0000 00d7        |</span><br><span class="line">      | .note.GNU|stack  |                   |</span><br><span class="line">      +------------------+0x0000 00d8        |</span><br><span class="line">      |     .eh_frame    |                   |</span><br><span class="line">      |                  |                   |</span><br><span class="line">      |                  |                   |</span><br><span class="line">      |                  |                   |</span><br><span class="line">      +------------------+0x0000 0130        |</span><br><span class="line">      |     .symtab      |                   |</span><br><span class="line">      |                  |                   |</span><br><span class="line">      |                  |                   |</span><br><span class="line">      |                  |                   |</span><br><span class="line">      +------------------+0x0000 02c8        |</span><br><span class="line">      |     .strtab      |                   |</span><br><span class="line">      |                  |                   |</span><br><span class="line">      |                  |                   |</span><br><span class="line">      |                  |                   |</span><br><span class="line">      +------------------+0x0000 0348        |</span><br><span class="line">      |    .rela.text    |                   |</span><br><span class="line">      |                  |                   |</span><br><span class="line">      |                  |                   |</span><br><span class="line">      +------------------+0x0000 03c0        |</span><br><span class="line">      |  .rela.eh_frame  |                   |</span><br><span class="line">      |                  |                   |</span><br><span class="line">      |                  |                   |</span><br><span class="line">      +------------------+0x0000 03f0        |</span><br><span class="line">      |     .shstrtab    |                   |</span><br><span class="line">      |                  |                   |</span><br><span class="line">      |               &lt;--+0x0000 0451        |</span><br><span class="line">      |                  |                   |</span><br><span class="line">      +------------------+0x0000 0458 &lt;------+</span><br><span class="line">      |                  |</span><br><span class="line">      |   Section Table  |</span><br><span class="line">      |                  |</span><br><span class="line">      |                  |</span><br><span class="line">      |                  |</span><br><span class="line">      |                  |</span><br><span class="line">      |                  |</span><br><span class="line"> end  +------------------+0x0000 0798</span><br></pre></td></tr></table></figure></li></ul><blockquote><p>文件大小:SimpleSection.o = 1944 = 0x798 Bit</p></blockquote><p><strong>段的名字只是在编译和链接过程中有意义,不能正真代表段的类型</strong></p><ul><li><code>.rela.text</code>: 重定位表(Relocation Table), 链接器处理目标文件时,对目标文件中的某些部位进行重定位,即代码段和数据段中那些绝对地址的引用位置.</li><li><code>.strtab</code>: 字符串表(String Table), 用于保存普通的字符串</li><li><code>.shstrtab</code>: 段表字符串表(Section Header String Table), 用于保存段表中用到的字符串</li></ul><h3 id="字符串表"><a href="#字符串表" class="headerlink" title="字符串表"></a>字符串表</h3><p>ELF文件中对字符串的存储,由于不确定其长度,没有固定的结构进行表示.常用的做法是将字符串集中起来存放在一个表中,使用字符串在表中的偏移来引用字符串</p><ul><li>字符串表:</li></ul><table><thead><tr><th style="text-align:center">偏移</th><th style="text-align:center">+0</th><th style="text-align:center">+1</th><th style="text-align:center">+2</th><th style="text-align:center">+3</th><th style="text-align:center">+4</th><th style="text-align:center">+5</th><th style="text-align:center">+6</th><th style="text-align:center">+7</th><th style="text-align:center">+8</th><th style="text-align:center">+9</th></tr></thead><tbody><tr><td style="text-align:center">+0</td><td style="text-align:center">\0</td><td style="text-align:center">h</td><td style="text-align:center">e</td><td style="text-align:center">l</td><td style="text-align:center">l</td><td style="text-align:center">o</td><td style="text-align:center">w</td><td style="text-align:center">o</td><td style="text-align:center">r</td><td style="text-align:center">l</td></tr><tr><td style="text-align:center">+10</td><td style="text-align:center">d</td><td style="text-align:center">\0</td><td style="text-align:center">M</td><td style="text-align:center">y</td><td style="text-align:center">v</td><td style="text-align:center">a</td><td style="text-align:center">r</td><td style="text-align:center">i</td><td style="text-align:center">a</td><td style="text-align:center">b</td></tr><tr><td style="text-align:center">+20</td><td style="text-align:center">l</td><td style="text-align:center">e</td><td style="text-align:center">\0</td><td style="text-align:center"></td><td style="text-align:center"></td><td style="text-align:center"></td><td style="text-align:center"></td><td style="text-align:center"></td><td style="text-align:center"></td></tr></tbody></table><ul><li>引用</li></ul><table><thead><tr><th style="text-align:center">偏移</th><th style="text-align:left">字符串</th></tr></thead><tbody><tr><td style="text-align:center">0</td><td style="text-align:left">空字符串</td></tr><tr><td style="text-align:center">1</td><td style="text-align:left">helloworld</td></tr><tr><td style="text-align:center">6</td><td style="text-align:left">wrold</td></tr><tr><td style="text-align:center">12</td><td style="text-align:left">Myvariable</td></tr></tbody></table><blockquote><p>字符串表在ELF中以段的形式保存</p></blockquote><table><thead><tr><th style="text-align:center">段</th><th style="text-align:left">段名</th><th style="text-align:left">含义</th></tr></thead><tbody><tr><td style="text-align:center"><code>.strtab</code></td><td style="text-align:left">字符串表(String Table)</td><td style="text-align:left">用于保存普通字符串</td></tr><tr><td style="text-align:center"><code>.shstrtab</code></td><td style="text-align:left">段表字符串表(Section Header String Table)</td><td style="text-align:left">用于保存段表中用到的字符串</td></tr></tbody></table><h2 id="链接的接口–符号"><a href="#链接的接口–符号" class="headerlink" title="链接的接口–符号"></a>链接的接口–符号</h2><blockquote><p>链接的本质就是把多个不同的目标文件(.o)之间相互”粘”到一起</p></blockquote><p>在链接中,将函数和变量统称为<code>符号</code>(Symbol),函数名和变量名统称为<code>符号名</code>(Symbol Name)</p><h3 id="特殊符号"><a href="#特殊符号" class="headerlink" title="特殊符号"></a>特殊符号</h3><table><thead><tr><th style="text-align:center">符号</th><th style="text-align:center">作用</th></tr></thead><tbody><tr><td style="text-align:center">__executable_start</td><td style="text-align:center">程序起始地址(注意不是程序入口地址,是程序最开始执行的地址)</td></tr><tr><td style="text-align:center">__etext_etext\etext</td><td style="text-align:center">代码段结束地址,即代码段最末尾的地址</td></tr><tr><td style="text-align:center">_edata\edata</td><td style="text-align:center">数据段结束地址</td></tr><tr><td style="text-align:center">_end\end</td><td style="text-align:center">程序结束地址</td></tr></tbody></table><blockquote><p>以上地址都是程序装载是的<code>虚拟地址</code></p></blockquote><h3 id="extern-“C”"><a href="#extern-“C”" class="headerlink" title="extern “C”"></a>extern “C”</h3><p>C++为了兼容C,在符号管理上使用<code>extern &quot;C&quot;</code></p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">extern</span> <span class="string">"C"</span> &#123;</span><br><span class="line">  <span class="function"><span class="keyword">int</span> <span class="title">func</span><span class="params">(<span class="keyword">int</span>)</span></span>;</span><br><span class="line">  <span class="keyword">int</span> var;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="弱符号和强符号"><a href="#弱符号和强符号" class="headerlink" title="弱符号和强符号"></a>弱符号和强符号</h3><table><thead><tr><th style="text-align:center">名称</th><th style="text-align:center">英文</th><th style="text-align:left">示例</th></tr></thead><tbody><tr><td style="text-align:center">弱符号</td><td style="text-align:center">Weak Symbol</td><td style="text-align:left">编译器默认,未初始化的全局变量</td></tr><tr><td style="text-align:center">强符号</td><td style="text-align:center">Strong Symbol</td><td style="text-align:left">编译器默认,函数和初始化的全局变量</td></tr></tbody></table><h4 id="弱符号的定义"><a href="#弱符号的定义" class="headerlink" title="弱符号的定义"></a>弱符号的定义</h4><p>通过GCC的<code>&quot;__attribute__((weak))&quot;</code>定义任何一个强符号为弱符号</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">extern</span> <span class="keyword">int</span> ext;</span><br><span class="line"></span><br><span class="line"><span class="keyword">int</span> weak;</span><br><span class="line"><span class="keyword">int</span> strong = <span class="number">1</span>;</span><br><span class="line">__attribute__((weak)) weak2 = <span class="number">2</span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><blockquote><ul><li><code>weak</code>和<code>waek2</code>是弱符号</li><li><code>strong</code>和<code>main</code>是强符号</li><li><code>ext</code>既非强符号也非弱符号,其是一个外部变量的引用</li></ul></blockquote><h4 id="链接器的处理规则"><a href="#链接器的处理规则" class="headerlink" title="链接器的处理规则"></a>链接器的处理规则</h4><ul><li>规则1: 不允许强符号多次定义.</li><li>规则2: 如果一个符号在某一个目标文件中定义为强符号,在其他目标文件中定义为弱符号,那么选择强符号.</li><li>规则3: 如果一个符号在所有目标文件中都是弱符号,那么选择其中占用空间最大的一个.</li></ul><h3 id="强引用-Strong-Reference-和弱引用-Weak-Reference"><a href="#强引用-Strong-Reference-和弱引用-Weak-Reference" class="headerlink" title="强引用(Strong Reference)和弱引用(Weak Reference)"></a>强引用(Strong Reference)和弱引用(Weak Reference)</h3><p>在GCC中，可以通过<code>__attribute__((weakref))</code>扩展关键字来声明一个外部函数的引用为弱引用</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">__attribute__((weakref)) <span class="function"><span class="keyword">void</span> <span class="title">foo</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong><code>弱符合</code>和<code>强符号</code>在库的定义使用中，弱符号可以被用户自定义的强符合所覆盖，从而使得程序可以使用自定义版本函数，方便程序扩展模块的裁剪和组合</strong></p><h2 id="静态链接-1"><a href="#静态链接-1" class="headerlink" title="静态链接"></a>静态链接</h2><blockquote><p>将相同性质的段合并到一起，如所有输入文件的<code>.text</code>段合并到输出文件的<code>.text</code>段</p></blockquote><h3 id="链接"><a href="#链接" class="headerlink" title="链接"></a>链接</h3><p>链接器一般采用<code>两步</code>链接：</p><ul><li>第一步： 空间与地址分配</li><li>第二部： 符号解析与定位</li></ul><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ld a.o b.o -e main -o ab</span><br></pre></td></tr></table></figure><blockquote><ul><li><code>-e main</code>: 表示将main函数作为程序入口，ld链接器默认的程序入口为<code>_start</code></li></ul></blockquote><h3 id="ld链接脚本"><a href="#ld链接脚本" class="headerlink" title="ld链接脚本"></a>ld链接脚本</h3><blockquote><p>控制链接过程，就是控制<code>输入段</code>如何变成<code>输出段</code>。</p></blockquote><ul><li>输入段（Input Section）： 输入文件中的段</li><li>输出段（Output Section）：输出文件中的段</li></ul><h4 id="ld链接脚本语法"><a href="#ld链接脚本语法" class="headerlink" title="ld链接脚本语法"></a>ld链接脚本语法</h4><p>链接脚本由一系列语句构成，语句分两种，一种是<code>命令语句</code>，另一种是<code>赋值语句</code>。</p><p>链接脚本语法与C语言相似：</p><ul><li>语句之间使用分号“<code>；</code>”作为分隔符</li><li>表达式与运算符<ul><li><code>+</code>、 <code>-</code>、 <code>*</code>、 <code>/</code>、 <code>+=</code>、 <code>-=</code>、 <code>*=</code>、 <code>&amp;</code>、 <code>|</code>、 <code>&gt;&gt;</code>、 <code>&lt;&lt;</code></li></ul></li><li>注释和字符引用<ul><li>注释：<code>/**/</code></li></ul></li></ul><p>命令语句：</p><table><thead><tr><th style="text-align:center">命令语句</th><th style="text-align:left">说明</th></tr></thead><tbody><tr><td style="text-align:center">ENTRY(symbol)</td><td style="text-align:left">指定符号的入口地址。入口地址即进程执行的第一条用户空间的指令所在进程地址空间的地址，它被指定在ELF文件头Elf32_Ehdr中的e_entry成员中。</td></tr><tr><td style="text-align:center">STARTUP(filename)</td><td style="text-align:left">将文件filename作为链接过程中的第一个输入文件</td></tr><tr><td style="text-align:center">SEARCH_DIR(path)</td><td style="text-align:left">将路径path加入到ld链接器的库查找目录。与“<code>-Lpath</code>”命令作用相同</td></tr><tr><td style="text-align:center">INPUT(file, file, …)</td><td style="text-align:left">将指定文件作为链接过程中的输入文件</td></tr><tr><td style="text-align:center">INCLUDE filename</td><td style="text-align:left">将指定文件包含到链接脚本</td></tr><tr><td style="text-align:center">PROVIDE(symbol)</td><td style="text-align:left">在链接脚本中定义某个符号</td></tr></tbody></table><p>语法格式：<br></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">SECTIONS</span><br><span class="line">&#123;</span><br><span class="line">    ...</span><br><span class="line">    secname : &#123;contents&#125;</span><br><span class="line">    ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><blockquote><p>secname: 表示输出段段名</p></blockquote><h4 id="示例"><a href="#示例" class="headerlink" title="示例"></a>示例</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">ENTRY(nomain)</span><br><span class="line">SECTIONS</span><br><span class="line">&#123;</span><br><span class="line">    - = 0x08048000 + SIZEOF_HEADERS;</span><br><span class="line"></span><br><span class="line">    tinytext : &#123; *(.text) *(.data) *(.rodata)&#125;</span><br><span class="line"></span><br><span class="line">    /DISCARD/ : &#123; *(.comment)&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>解析：</p><ul><li><code>ENTRY(nomain)</code>: 指定程序入口为nomain()函数</li><li><code>SECTIONS</code>： 链接脚本主体</li><li><code>. = 0x08048000 + SIZEOF_HEADERS</code>：将当前虚拟地址设置成0x08048000+SIZEOF_HEADERS, SIZEOF_HEADERS为输出文件头大小。</li><li><code>tinytext : { *(.text) *(.data) *(.rodata)}</code>： 段的转换，即为所以输入文件中的名字为”.text” “.data” “.rodata”的段依次合并到输出文件的”tinytext”。</li><li><code>/DISCARD/ : { *(.comment)}</code>： 将所有输入文件中的”.comment”的段丢弃，不保存到输出文件</li></ul><hr><h1 id="装载与动态链接"><a href="#装载与动态链接" class="headerlink" title="装载与动态链接"></a>装载与动态链接</h1><h2 id="可执行文件的装载与进程"><a href="#可执行文件的装载与进程" class="headerlink" title="可执行文件的装载与进程"></a>可执行文件的装载与进程</h2><h3 id="进程的虚拟地址空间"><a href="#进程的虚拟地址空间" class="headerlink" title="进程的虚拟地址空间"></a>进程的虚拟地址空间</h3><p>虚拟地址空间（Virtual Address Space）大小，有CPU位数决定。</p><p><code>PAE</code>： （Physical Address Extension）物理地址扩展，主要是为了解决32位CPU中通过增加地址线扩展内存而导致的实际物理内存大小与虚拟地址空间大小之间的不匹配问题</p><blockquote><p>PAE，通过<code>页映射</code>改变不同时机对不同物理空间的访问机制， 在Linux系统中采用<code>mmap()</code>系统调用实现。</p></blockquote><h3 id="装载方式"><a href="#装载方式" class="headerlink" title="装载方式"></a>装载方式</h3><p>装载方式有<code>静态装载</code>和<code>动态装载</code>，大部分使用动态装载的方式，动态装载分为<code>覆盖装入（Overlay）</code>和<code>页映射（Paging）</code></p><ul><li><code>覆盖装入</code>：需要程序员自己编写覆盖管理器，进行程序的划分和装载管理</li><li><code>页映射</code>：以页大小将程序进行划分后，有操作系统进行装入的管理</li></ul><h3 id="页映射"><a href="#页映射" class="headerlink" title="页映射"></a>页映射</h3><p>将内存和所有磁盘中的数据和指令按照<code>页（Page）</code>为单位进行划分后，在需要时进行动态映射的过程。</p><blockquote><p>页大小，硬件规定页大小4k、8K、2M、4M等，常用<code>4K</code>大小</p></blockquote><p><img src="/images/2019/03/程序员的自我修养_page_map.png" alt="程序员的自我修养_page_map"></p><p>页的置换：</p><ul><li>先进先出算法（FIFO）：置换最先映射到页</li><li>最少使用算法（LUR）：将内存中很少访问到的页置换出去</li></ul><h3 id="进程的虚拟空间分布"><a href="#进程的虚拟空间分布" class="headerlink" title="进程的虚拟空间分布"></a>进程的虚拟空间分布</h3><p><code>segment</code>：从装载的角度重新划分了ELF的各个段，一个“segment”包含一个或多个属性类似的”Section”</p><p>segment的好处，减少页面内部碎片，节省内存空间。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line">readelf -l a.out</span><br><span class="line"></span><br><span class="line">Elf file type is DYN (Shared object file)</span><br><span class="line">Entry point 0x540</span><br><span class="line">There are 9 program headers, starting at offset 64</span><br><span class="line"></span><br><span class="line">Program Headers:</span><br><span class="line">  Type           Offset             VirtAddr           PhysAddr</span><br><span class="line">                 FileSiz            MemSiz              Flags  Align</span><br><span class="line">  PHDR           0x0000000000000040 0x0000000000000040 0x0000000000000040</span><br><span class="line">                 0x00000000000001f8 0x00000000000001f8  R      0x8</span><br><span class="line">  INTERP         0x0000000000000238 0x0000000000000238 0x0000000000000238</span><br><span class="line">                 0x000000000000001c 0x000000000000001c  R      0x1</span><br><span class="line">      [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]</span><br><span class="line">  LOAD           0x0000000000000000 0x0000000000000000 0x0000000000000000</span><br><span class="line">                 0x0000000000000878 0x0000000000000878  R E    0x200000</span><br><span class="line">  LOAD           0x0000000000000db8 0x0000000000200db8 0x0000000000200db8</span><br><span class="line">                 0x0000000000000258 0x0000000000000260  RW     0x200000</span><br><span class="line">  DYNAMIC        0x0000000000000dc8 0x0000000000200dc8 0x0000000000200dc8</span><br><span class="line">                 0x00000000000001f0 0x00000000000001f0  RW     0x8</span><br><span class="line">  NOTE           0x0000000000000254 0x0000000000000254 0x0000000000000254</span><br><span class="line">                 0x0000000000000044 0x0000000000000044  R      0x4</span><br><span class="line">  GNU_EH_FRAME   0x0000000000000730 0x0000000000000730 0x0000000000000730</span><br><span class="line">                 0x000000000000003c 0x000000000000003c  R      0x4</span><br><span class="line">  GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000</span><br><span class="line">                 0x0000000000000000 0x0000000000000000  RW     0x10</span><br><span class="line">  GNU_RELRO      0x0000000000000db8 0x0000000000200db8 0x0000000000200db8</span><br><span class="line">                 0x0000000000000248 0x0000000000000248  R      0x1</span><br><span class="line"></span><br><span class="line"> Section to Segment mapping:</span><br><span class="line">  Segment Sections...</span><br><span class="line">   00</span><br><span class="line">   01     .interp</span><br><span class="line">   02     .interp .note.ABI-tag .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt .init .plt .plt.got .text .fini .rodata .eh_frame_hdr .eh_frame</span><br><span class="line">   03     .init_array .fini_array .dynamic .got .data .bss</span><br><span class="line">   04     .dynamic</span><br><span class="line">   05     .note.ABI-tag .note.gnu.build-id</span><br><span class="line">   06     .eh_frame_hdr</span><br><span class="line">   07</span><br><span class="line">   08     .init_array .fini_array .dynamic .got</span><br></pre></td></tr></table></figure><ul><li><code>section</code>：ELF文件的链接视图</li><li><p><code>segment</code>：ELF文件的执行视图</p></li><li><p><code>VMA</code>：（Virtual Memory Area）虚拟内存区域</p></li><li><code>AVMA</code>：（Anonvmous Virtual Memory Area）匿名虚拟内存区域，主设备号、次设备号以及文件节点号都是0，表示没有映射到文件中的VMA</li></ul><h3 id="随机地址空间分布技术"><a href="#随机地址空间分布技术" class="headerlink" title="随机地址空间分布技术"></a>随机地址空间分布技术</h3><h3 id="段地址对齐"><a href="#段地址对齐" class="headerlink" title="段地址对齐"></a>段地址对齐</h3><p>物理内存与虚拟进程之间的映射关系，内存空间的<code>长度</code>必须是<code>4096</code>的整数倍，并且这段空间的物理内存和进程虚拟地址空间的<code>起始地址</code>必须是<code>4096</code>的整数倍。</p><p>提高内存空间的利用率。</p><h3 id="Linux内核装载ELF过程"><a href="#Linux内核装载ELF过程" class="headerlink" title="Linux内核装载ELF过程"></a>Linux内核装载ELF过程</h3><ol><li>bash进程调用<code>fork()</code>系统调用创建一个新的进程</li><li>新进程调用<code>execve()</code>系统调用执行指定的ELF文件<ol><li>execve -&gt; SYSCALL_DEFINE3(execve, …) -&gt; do_execve</li><li>do_execve中判断ELF文件的类型，不同的类型（C，Java等）选择不同的装载过程，装载ELF文件，并且do_execve系统调用返回的地址就是被装载的ELF程序的入口地址</li><li><code>execve</code>系统调用从内核态返回用户态时，EIP（PC）寄存器直接跳转到ELF程序的入口地址，执行新程序</li></ol></li></ol><h2 id="动态链接"><a href="#动态链接" class="headerlink" title="动态链接"></a>动态链接</h2><ul><li><code>静态链接</code>好处，使不同的开发者能够独立的开发和测试自己的程序模块，不好之处在于，对计算机的内存和磁盘空间浪费严重， 同时也不利于模块更新。</li><li><code>动态链接</code>，主要是为解决<code>空间浪费</code>和<code>更新困难</code>，其思想是将链接过程推迟到运行时进行，好处节省内存，减少物理页面的换入换出，增加CPU缓存的命中率</li></ul><p>动态链接模块的装载地址是从地址<code>0x00000000</code>开始，无效的地址，真实的地址是在装载时确定的。</p><h3 id="装载时重定位"><a href="#装载时重定位" class="headerlink" title="装载时重定位"></a>装载时重定位</h3><blockquote><p>为了能够使共享对象能够在任意地址装载</p></blockquote><ul><li>静态链接时的重定位，<code>链接时重定位（Link Time Relocation）</code></li><li>动态链接时的重定位，<code>装载时重定位（Load Time Relocation）</code>,也叫<code>基址重置（Rebasing）</code></li></ul><p>动态链接模块被装载映射到虚拟内存后，指令部分是在多个进程之间共享的，由于装载时重定位是需要修改指令，所以没有办法做到同一份指令被多进程共享</p><p>动态链接库中的可修改数据部分对于不同的进程来说有多个副本，所以他们可以采用装载是重定位的方法解决。</p><h3 id="地址无关代码"><a href="#地址无关代码" class="headerlink" title="地址无关代码"></a>地址无关代码</h3><blockquote><p><code>-fPIC</code>, 选择地址无关代码（Position-independent Code）</p></blockquote><p>数据段GOT表（全局偏移表，Global Offset Table）的引入，就是把地址相关部分放到数据段里。</p><ul><li>地址引用方式</li></ul><table><thead><tr><th style="text-align:center"></th><th style="text-align:center">指令跳转、调用</th><th style="text-align:center">数据访问</th></tr></thead><tbody><tr><td style="text-align:center">模块内部</td><td style="text-align:center">相对跳转和调用</td><td style="text-align:center">相对地址访问</td></tr><tr><td style="text-align:center">模块外部</td><td style="text-align:center">间接跳转和调用（GOT）</td><td style="text-align:center">间接访问（GOT）</td></tr></tbody></table><p>ELF共享库编译时，默认把定义在模块内部的全局变量当做定义在其他模块的全局变量，通过GOT表实现数据访问</p><h3 id="数据段地址无关性"><a href="#数据段地址无关性" class="headerlink" title="数据段地址无关性"></a>数据段地址无关性</h3><p>装载时重定位数据段</p><h3 id="fPIC"><a href="#fPIC" class="headerlink" title="-fPIC"></a>-fPIC</h3><p>编译选项：</p><ul><li>添加： 表示产生地址无关的代码段，这样生成的动态链接的可执行文件中存在<code>GOT</code>表，代码段共享节省空间</li><li>不添加：表示装载时使用重定位共享对象，代码段多份拷贝，不能节省空间，但是运行速度要比使用地址无关代码的共享对象快，因为它省去了地址无关代码中每次访问全局数据和函数时需要做一次计算当前地址以及间接地址的寻址过程。</li></ul><h3 id="延迟绑定（PLT）"><a href="#延迟绑定（PLT）" class="headerlink" title="延迟绑定（PLT）"></a>延迟绑定（PLT）</h3><blockquote><p><code>PLT</code>: Procedure Linkage Table(程序链接表)</p></blockquote><p>ELF将GOT拆分为两个表叫做<code>“.got”</code>和<code>”.got.plt&quot;</code></p><ul><li><code>.got</code>: 用于保存全局变量引用的地址</li><li><code>.got.plt</code>： 用于保存函数引用的地址</li></ul><h3 id="interp段"><a href="#interp段" class="headerlink" title=".interp段"></a><code>.interp</code>段</h3><p>动态链接器的<code>位置</code>既不是系统配置指定，也不是由环境参数决定，而由ELF可执行文件决定，在ELF文件的<code>.interp</code>段中定义（interp是interpreter（解释器）的缩写）</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$readelf -l a.out | grep &quot;interpreter&quot;</span><br><span class="line">      [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]</span><br></pre></td></tr></table></figure><h3 id="dynamic段"><a href="#dynamic段" class="headerlink" title=".dynamic段"></a><code>.dynamic</code>段</h3><p>保存链接器所需的基本信息，如依赖哪些共享对象、动态链接符号表的位置、动态链接重定位表的位置、共享对象初始化代码的地址</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">readelf -d libxx.so</span><br></pre></td></tr></table></figure><h3 id="动态符号表"><a href="#动态符号表" class="headerlink" title="动态符号表"></a>动态符号表</h3><p>动态符号表（Dynamic Symbol Table），为表示动态链接时模块之间的导入导出关系，段名<code>.dynsym</code></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">readelf -sD libxx.so</span><br></pre></td></tr></table></figure><h3 id="动态链接重定位表"><a href="#动态链接重定位表" class="headerlink" title="动态链接重定位表"></a>动态链接重定位表</h3><p>PIC模式的共享对象也需要<code>重定位</code>，其代码段是地址无关，不需要重定位，但是数据段其中包含决定地址并且GOT表也在数据段</p><h3 id="动态链接器的自举"><a href="#动态链接器的自举" class="headerlink" title="动态链接器的自举"></a>动态链接器的自举</h3><p>动态链接器本身是一个共享对象，但是其具有特殊性<strong>不可以依赖其他任何对象，其本身所需要的全局变量和静态变量的重定位工作由它本身完成</strong></p><p><code>自举（bootstrap）</code>：具有一定限制条件的启动代码</p><p>动态链接器的入口地址是自举代码的入口地址。</p><h3 id="显石运行时链接"><a href="#显石运行时链接" class="headerlink" title="显石运行时链接"></a>显石运行时链接</h3><p>动态库的装载API：<code>#include &lt;dlfcn.h&gt;</code></p><ul><li><code>dlopen</code>：打开动态库</li><li><code>dlsym</code>：查找符号</li><li><code>dlerror</code>：错误处理</li><li><code>dlclose</code>：关闭动态库</li></ul><h2 id="Linux共享库的组织"><a href="#Linux共享库的组织" class="headerlink" title="Linux共享库的组织"></a>Linux共享库的组织</h2><p>兼容即接口，二进制接口（<code>ABI</code>，Application Binary Interface）</p><h3 id="共享库版本命名"><a href="#共享库版本命名" class="headerlink" title="共享库版本命名"></a>共享库版本命名</h3><blockquote><p>libname.so.z.y.z</p></blockquote><ul><li><code>x</code>:主版本号（Major Version Number），表示库的重大升级，不同主版本号之间不兼容</li><li><code>y</code>:次版本号（Minor Version Number），表示库的增量升级，高的次版本号的库向后兼容低的次版本号的库</li><li><code>z</code>:发布版本号（Release Version Number），表示库的错误修正、性能改进等，不添加如何新的接口也不对任何接口进行改动</li></ul><h3 id="SO-NAME"><a href="#SO-NAME" class="headerlink" title="SO-NAME"></a>SO-NAME</h3><blockquote><p>共享库的命名机制，即共享库的文件名去掉次版本号和发布版本号，保留主版本号，本质就是一个<code>软链接</code></p></blockquote><h3 id="共享库查找过程"><a href="#共享库查找过程" class="headerlink" title="共享库查找过程"></a>共享库查找过程</h3><p>动态链接器会在<code>/lib</code>、<code>/user/lib</code>和由<code>/etc/ld.so.conf</code>配置文件指定的目录中查找共享库</p><h3 id="环境变量"><a href="#环境变量" class="headerlink" title="环境变量"></a>环境变量</h3><h4 id="LD-LIBRARY-PATH"><a href="#LD-LIBRARY-PATH" class="headerlink" title="LD_LIBRARY_PATH"></a>LD_LIBRARY_PATH</h4><blockquote><p>改变动态链接器装载共享库路径的方法</p></blockquote><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">export LD_LIBRARY_PATH = /xxxx/libname.so.x</span><br></pre></td></tr></table></figure><h4 id="LD-PRELOAD"><a href="#LD-PRELOAD" class="headerlink" title="LD_PRELOAD"></a>LD_PRELOAD</h4><blockquote><p>指定预先要装载的共享库路径</p></blockquote><h4 id="LD-DEBUG"><a href="#LD-DEBUG" class="headerlink" title="LD_DEBUG"></a>LD_DEBUG</h4><blockquote><p>打开动态链接器调试功能</p></blockquote><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">LD_DEBUG=files ./a.out</span><br><span class="line">      1696:</span><br><span class="line">      1696:	WARNING: Unsupported flag value(s) of 0x8000000 in DT_FLAGS_1.</span><br><span class="line">      1696:</span><br><span class="line">      1696:	file=libc.so.6 [0];  needed by ./a.out [0]</span><br><span class="line">      1696:	file=libc.so.6 [0];  generating link map</span><br><span class="line">      1696:	  dynamic: 0x00007f36e1202b80  base: 0x00007f36e0e18000   size: 0x00000000003f0ae0</span><br><span class="line">      1696:	    entry: 0x00007f36e0e39cb0  phdr: 0x00007f36e0e18040  phnum:                 10</span><br><span class="line">      1696:</span><br><span class="line">      1696:</span><br><span class="line">      1696:	calling init: /lib/x86_64-linux-gnu/libc.so.6</span><br><span class="line">      1696:</span><br><span class="line">      1696:</span><br><span class="line">      1696:	initialize program: ./a.out</span><br><span class="line">      1696:</span><br><span class="line">      1696:</span><br><span class="line">      1696:	transferring control: ./a.out</span><br><span class="line">      1696:</span><br></pre></td></tr></table></figure><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">LD_DEBUG=help ./a.out</span><br><span class="line">Valid options for the LD_DEBUG environment variable are:</span><br><span class="line"></span><br><span class="line">  libs        display library search paths</span><br><span class="line">  reloc       display relocation processing</span><br><span class="line">  files       display progress for input file</span><br><span class="line">  symbols     display symbol table processing</span><br><span class="line">  bindings    display information about symbol binding</span><br><span class="line">  versions    display version dependencies</span><br><span class="line">  scopes      display scope information</span><br><span class="line">  all         all previous options combined</span><br><span class="line">  statistics  display relocation statistics</span><br><span class="line">  unused      determined unused DSOs</span><br><span class="line">  help        display this help message and exit</span><br><span class="line"></span><br><span class="line">To direct the debugging output into a file instead of standard output</span><br><span class="line">a filename can be specified using the LD_DEBUG_OUTPUT environment variable.</span><br></pre></td></tr></table></figure><hr><h1 id="库与运行库"><a href="#库与运行库" class="headerlink" title="库与运行库"></a>库与运行库</h1><h2 id="内存"><a href="#内存" class="headerlink" title="内存"></a>内存</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line">+-----------------------+ 0xffff ffff</span><br><span class="line">|                       |</span><br><span class="line">|     kernel space      |</span><br><span class="line">|                       |</span><br><span class="line">+-----------------------+ 0xc000 0000</span><br><span class="line">|         stack         |</span><br><span class="line">+-----------+-----------+</span><br><span class="line">|           |           |</span><br><span class="line">|           v           |</span><br><span class="line">|         unused        |</span><br><span class="line">+-----------------------+</span><br><span class="line">|  dynamic libraries    |</span><br><span class="line">+-----------------------+ 0x4000 0000</span><br><span class="line">|         unused        |</span><br><span class="line">|           ^           |</span><br><span class="line">|           |           |</span><br><span class="line">+-----------+-----------+</span><br><span class="line">|          heap         |</span><br><span class="line">+-----------------------+</span><br><span class="line">|  read/write sections  |</span><br><span class="line">+-----------------------+</span><br><span class="line">|  readonly sections    |</span><br><span class="line">|   .init .rodata .text |</span><br><span class="line">+-----------------------+ 0x0804 8000</span><br><span class="line">|       reserved        |</span><br><span class="line">+-----------------------+ 0</span><br></pre></td></tr></table></figure><blockquote><p>Linux进程地址空间分布</p></blockquote><h3 id="Segment-fault"><a href="#Segment-fault" class="headerlink" title="Segment fault"></a>Segment fault</h3><p>原因：</p><ul><li>指针初始化为NULL，没有指定空间直接开始使用</li><li>没有初始化栈上的指针，直接开始使用，该指针一般是一个随机数</li></ul><h3 id="栈"><a href="#栈" class="headerlink" title="栈"></a>栈</h3><blockquote><p>在经典的计算机科学中，<code>栈</code>是一种特殊的容器，具有先进先出（FIFO）的规则，但是在计算机系统中，栈则是一个具有<code>FIFO</code>属性的内存区域</p></blockquote><p>栈保存了一个函数调用所需的维护信息，通常称为<code>堆栈帧</code>（Stack Frame）或<code>活动记录</code>（Activate Record）</p><p>堆栈帧的内容：</p><ul><li>函数的返回地址和参数</li><li>临时变量，包括函数的非静态变量以及编译器生成的其他临时变量</li><li>保存的上下文，包含函数调用前后保持不变的寄存器</li></ul><h3 id="堆"><a href="#堆" class="headerlink" title="堆"></a>堆</h3><h4 id="mmap"><a href="#mmap" class="headerlink" title="mmap"></a>mmap</h4><blockquote><p>向操作系统申请一段虚拟地址空间，这段空间可以映射到一个文件，也可以不用射文件，不映射文件的时候叫<code>匿名空间</code></p></blockquote><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;sys/mman.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> *<span class="title">mmap</span><span class="params">(<span class="keyword">void</span> *addr, <span class="keyword">size_t</span> length, <span class="keyword">int</span> prot, <span class="keyword">int</span> flags,</span></span></span><br><span class="line"><span class="function"><span class="params">           <span class="keyword">int</span> fd, <span class="keyword">off_t</span> offset)</span></span>;</span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">munmap</span><span class="params">(<span class="keyword">void</span> *addr, <span class="keyword">size_t</span> length)</span></span>;</span><br></pre></td></tr></table></figure><ul><li><code>addr</code>: 申请空间的起始地址，如果为<code>0</code>表示Linux系统会自动选择合适的起始地址</li><li><code>length</code>: 申请空间的长度</li><li><code>prot</code>: 设置申请的空间权限（可读、可写、可执行）</li><li><code>flags</code>：设置申请空间的映射类型（文件类型，匿名空间）</li><li><code>fd</code>：表示映射的文件描述符</li><li><code>offset</code>： 指定文件映射的文件偏移</li></ul><p>glibc的malloc函数处理用户空间请求：对于小于128KB的请求，它会在现有堆空间里面，按照堆分配算法为它分配一块空间并返回；对于大于128KB的请求来说，会使用mmap分配一块匿名空间，然后在这块匿名空间中为用户分配空间。</p><h4 id="堆分配算法"><a href="#堆分配算法" class="headerlink" title="堆分配算法"></a>堆分配算法</h4><ul><li>空闲链表</li><li>位图</li><li>对象池</li></ul><h2 id="运行库"><a href="#运行库" class="headerlink" title="运行库"></a>运行库</h2><h2 id="系统调用与API"><a href="#系统调用与API" class="headerlink" title="系统调用与API"></a>系统调用与API</h2><h2 id="运行库实现"><a href="#运行库实现" class="headerlink" title="运行库实现"></a>运行库实现</h2><hr><h1 id="工具"><a href="#工具" class="headerlink" title="工具"></a>工具</h1><h2 id="gcc"><a href="#gcc" class="headerlink" title="gcc"></a>gcc</h2><h3 id="fno-builtin"><a href="#fno-builtin" class="headerlink" title="-fno-builtin"></a>-fno-builtin</h3><blockquote><p>关闭内置函数优化选项</p></blockquote><h2 id="objdump"><a href="#objdump" class="headerlink" title="objdump"></a>objdump</h2><h2 id="objcopy"><a href="#objcopy" class="headerlink" title="objcopy"></a>objcopy</h2><h2 id="readelf"><a href="#readelf" class="headerlink" title="readelf"></a>readelf</h2><h2 id="nm"><a href="#nm" class="headerlink" title="nm"></a>nm</h2><h2 id="strip"><a href="#strip" class="headerlink" title="strip"></a>strip</h2><blockquote><p>清除掉共享库或可执行文件的所有符号和调试信息</p></blockquote><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">strip libname.so.x</span><br></pre></td></tr></table></figure><h2 id="ar"><a href="#ar" class="headerlink" title="ar"></a>ar</h2><blockquote><p>查看函数库里的详细情况和用多个对象文件生成一个库文件</p></blockquote><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">$ar</span><br><span class="line">Usage: ar [emulation options] [-]&#123;dmpqrstx&#125;[abcDfilMNoPsSTuvV] [--plugin &lt;name&gt;] [member-name] [count] archive-file file...</span><br><span class="line">       ar -M [&lt;mri-script]</span><br><span class="line"> commands:</span><br><span class="line">  d            - delete file(s) from the archive</span><br><span class="line">  m[ab]        - move file(s) in the archive</span><br><span class="line">  p            - print file(s) found in the archive</span><br><span class="line">  q[f]         - quick append file(s) to the archive</span><br><span class="line">  r[ab][f][u]  - replace existing or insert new file(s) into the archive</span><br><span class="line">  s            - act as ranlib</span><br><span class="line">  t            - display contents of archive</span><br><span class="line">  x[o]         - extract file(s) from the archive</span><br></pre></td></tr></table></figure><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ar -t libname.a</span><br></pre></td></tr></table></figure><blockquote><p>显示所有对象文件(.o文件)的列表</p></blockquote><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ar -rv libname.a  objfile1.o objfile2.o ... objfilen.o</span><br></pre></td></tr></table></figure><blockquote><p>把objfile1.o–objfilen.o打包成一个库文件</p></blockquote><h2 id="ld"><a href="#ld" class="headerlink" title="ld"></a>ld</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">ld --help</span><br><span class="line">Usage: ld [options] file...</span><br><span class="line">Options:</span><br><span class="line">  -e ADDRESS: 指定程序入口</span><br><span class="line">  -T FILE：读取链接脚本（*.ld）</span><br></pre></td></tr></table></figure><h2 id="ldd"><a href="#ldd" class="headerlink" title="ldd"></a>ldd</h2><blockquote><p>查看一个程序主模块或共享库依赖哪些共享库</p></blockquote><h2 id="ldconfig"><a href="#ldconfig" class="headerlink" title="ldconfig"></a>ldconfig</h2><blockquote><p>为共享库目录下的各个共享库创建、删除或更新相应的SO-NAME</p></blockquote><ul><li>安装</li></ul><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ldconfig -n libname.so.x</span><br></pre></td></tr></table></figure><hr></div></div><ul class="breadcrumb"><li><a href="/books/">BOOKS</a></li><li>程序员的自我修养</li></ul></div></div><script>
  window.addEventListener('tabs:register', () => {
    let activeClass = CONFIG.comments.activeClass;
    if (CONFIG.comments.storage) {
      activeClass = localStorage.getItem('comments_active') || activeClass;
    }
    if (activeClass) {
      let activeTab = document.querySelector(`a[href="#comment-${activeClass}"]`);
      if (activeTab) {
        activeTab.click();
      }
    }
  });
  if (CONFIG.comments.storage) {
    window.addEventListener('tabs:click', event => {
      if (!event.target.matches('.tabs-comment .tab-content .tab-pane')) return;
      let commentClass = event.target.classList[1];
      localStorage.setItem('comments_active', commentClass);
    });
  }
</script></div><div class="toggle sidebar-toggle"><span class="toggle-line toggle-line-first"></span><span class="toggle-line toggle-line-middle"></span><span class="toggle-line toggle-line-last"></span></div><aside class="sidebar"><div class="sidebar-inner"><ul class="sidebar-nav motion-element"><li class="sidebar-nav-toc"> 文章目录</li><li class="sidebar-nav-overview"> 站点概览</li></ul><div class="post-toc-wrap sidebar-panel"><div class="post-toc motion-element"><ol class="nav"><li class="nav-item nav-level-1"><a class="nav-link" href="#温故而知新"><span class="nav-number">1.</span> <span class="nav-text">温故而知新</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#内存不够怎么办"><span class="nav-number">1.1.</span> <span class="nav-text">内存不够怎么办</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#隔离"><span class="nav-number">1.2.</span> <span class="nav-text">隔离</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#分段-Segmentation"><span class="nav-number">1.2.1.</span> <span class="nav-text">分段(Segmentation)</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#分页-Paging"><span class="nav-number">1.2.2.</span> <span class="nav-text">分页(Paging)</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#Page-Fault"><span class="nav-number">1.2.2.1.</span> <span class="nav-text">Page Fault</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#页映射–数据保护"><span class="nav-number">1.2.2.2.</span> <span class="nav-text">页映射–数据保护</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#MMU-Memory-Management-Unit"><span class="nav-number">1.2.2.3.</span> <span class="nav-text">MMU (Memory Management Unit)</span></a></li></ol></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#线程-–-Thread"><span class="nav-number">1.3.</span> <span class="nav-text">线程 – Thread</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#线程的访问权限"><span class="nav-number">1.3.1.</span> <span class="nav-text">线程的访问权限</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#线程调度"><span class="nav-number">1.3.2.</span> <span class="nav-text">线程调度</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#线程安全"><span class="nav-number">1.3.3.</span> <span class="nav-text">线程安全</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#竞争与原子操作"><span class="nav-number">1.3.3.1.</span> <span class="nav-text">竞争与原子操作</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#锁与同步"><span class="nav-number">1.3.3.2.</span> <span class="nav-text">锁与同步</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#可重入-Reentrant-与线程安全"><span class="nav-number">1.3.4.</span> <span class="nav-text">可重入(Reentrant)与线程安全</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#过度优化"><span class="nav-number">1.3.5.</span> <span class="nav-text">过度优化</span></a></li></ol></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#静态链接"><span class="nav-number">2.</span> <span class="nav-text">静态链接</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#GCC编译过程"><span class="nav-number">2.1.</span> <span class="nav-text">GCC编译过程</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#预编译-Prepressing"><span class="nav-number">2.1.1.</span> <span class="nav-text">预编译-Prepressing</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#编译-Compilation"><span class="nav-number">2.1.2.</span> <span class="nav-text">编译-Compilation</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#汇编-Assembly"><span class="nav-number">2.1.3.</span> <span class="nav-text">汇编-Assembly</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#链接-Linking"><span class="nav-number">2.1.4.</span> <span class="nav-text">链接-Linking</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#示例-Gcc-7-3-0"><span class="nav-number">2.1.5.</span> <span class="nav-text">示例: Gcc 7.3.0</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#目标文件–Object-File"><span class="nav-number">2.2.</span> <span class="nav-text">目标文件–Object File</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#文件格式-ELF"><span class="nav-number">2.2.1.</span> <span class="nav-text">文件格式-ELF</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#ELF的格式"><span class="nav-number">2.2.2.</span> <span class="nav-text">ELF的格式</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#数据和指令分段的好处"><span class="nav-number">2.2.3.</span> <span class="nav-text">数据和指令分段的好处</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#挖掘SimpleSection-o"><span class="nav-number">2.3.</span> <span class="nav-text">挖掘SimpleSection.o</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#ELF的结构"><span class="nav-number">2.3.1.</span> <span class="nav-text">ELF的结构</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#代码段"><span class="nav-number">2.3.2.</span> <span class="nav-text">代码段</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#数据段和只读数据段"><span class="nav-number">2.3.3.</span> <span class="nav-text">数据段和只读数据段</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#BBS段"><span class="nav-number">2.3.4.</span> <span class="nav-text">BBS段</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#其他段"><span class="nav-number">2.3.5.</span> <span class="nav-text">其他段</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#自定义段"><span class="nav-number">2.3.6.</span> <span class="nav-text">自定义段</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#ELF文件结构描述"><span class="nav-number">2.4.</span> <span class="nav-text">ELF文件结构描述</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#头文件"><span class="nav-number">2.4.1.</span> <span class="nav-text">头文件</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#段表"><span class="nav-number">2.4.2.</span> <span class="nav-text">段表</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#字符串表"><span class="nav-number">2.4.3.</span> <span class="nav-text">字符串表</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#链接的接口–符号"><span class="nav-number">2.5.</span> <span class="nav-text">链接的接口–符号</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#特殊符号"><span class="nav-number">2.5.1.</span> <span class="nav-text">特殊符号</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#extern-“C”"><span class="nav-number">2.5.2.</span> <span class="nav-text">extern “C”</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#弱符号和强符号"><span class="nav-number">2.5.3.</span> <span class="nav-text">弱符号和强符号</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#弱符号的定义"><span class="nav-number">2.5.3.1.</span> <span class="nav-text">弱符号的定义</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#链接器的处理规则"><span class="nav-number">2.5.3.2.</span> <span class="nav-text">链接器的处理规则</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#强引用-Strong-Reference-和弱引用-Weak-Reference"><span class="nav-number">2.5.4.</span> <span class="nav-text">强引用(Strong Reference)和弱引用(Weak Reference)</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#静态链接-1"><span class="nav-number">2.6.</span> <span class="nav-text">静态链接</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#链接"><span class="nav-number">2.6.1.</span> <span class="nav-text">链接</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#ld链接脚本"><span class="nav-number">2.6.2.</span> <span class="nav-text">ld链接脚本</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#ld链接脚本语法"><span class="nav-number">2.6.2.1.</span> <span class="nav-text">ld链接脚本语法</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#示例"><span class="nav-number">2.6.2.2.</span> <span class="nav-text">示例</span></a></li></ol></li></ol></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#装载与动态链接"><span class="nav-number">3.</span> <span class="nav-text">装载与动态链接</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#可执行文件的装载与进程"><span class="nav-number">3.1.</span> <span class="nav-text">可执行文件的装载与进程</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#进程的虚拟地址空间"><span class="nav-number">3.1.1.</span> <span class="nav-text">进程的虚拟地址空间</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#装载方式"><span class="nav-number">3.1.2.</span> <span class="nav-text">装载方式</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#页映射"><span class="nav-number">3.1.3.</span> <span class="nav-text">页映射</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#进程的虚拟空间分布"><span class="nav-number">3.1.4.</span> <span class="nav-text">进程的虚拟空间分布</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#随机地址空间分布技术"><span class="nav-number">3.1.5.</span> <span class="nav-text">随机地址空间分布技术</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#段地址对齐"><span class="nav-number">3.1.6.</span> <span class="nav-text">段地址对齐</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Linux内核装载ELF过程"><span class="nav-number">3.1.7.</span> <span class="nav-text">Linux内核装载ELF过程</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#动态链接"><span class="nav-number">3.2.</span> <span class="nav-text">动态链接</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#装载时重定位"><span class="nav-number">3.2.1.</span> <span class="nav-text">装载时重定位</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#地址无关代码"><span class="nav-number">3.2.2.</span> <span class="nav-text">地址无关代码</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#数据段地址无关性"><span class="nav-number">3.2.3.</span> <span class="nav-text">数据段地址无关性</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#fPIC"><span class="nav-number">3.2.4.</span> <span class="nav-text">-fPIC</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#延迟绑定（PLT）"><span class="nav-number">3.2.5.</span> <span class="nav-text">延迟绑定（PLT）</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#interp段"><span class="nav-number">3.2.6.</span> <span class="nav-text">.interp段</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#dynamic段"><span class="nav-number">3.2.7.</span> <span class="nav-text">.dynamic段</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#动态符号表"><span class="nav-number">3.2.8.</span> <span class="nav-text">动态符号表</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#动态链接重定位表"><span class="nav-number">3.2.9.</span> <span class="nav-text">动态链接重定位表</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#动态链接器的自举"><span class="nav-number">3.2.10.</span> <span class="nav-text">动态链接器的自举</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#显石运行时链接"><span class="nav-number">3.2.11.</span> <span class="nav-text">显石运行时链接</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Linux共享库的组织"><span class="nav-number">3.3.</span> <span class="nav-text">Linux共享库的组织</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#共享库版本命名"><span class="nav-number">3.3.1.</span> <span class="nav-text">共享库版本命名</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#SO-NAME"><span class="nav-number">3.3.2.</span> <span class="nav-text">SO-NAME</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#共享库查找过程"><span class="nav-number">3.3.3.</span> <span class="nav-text">共享库查找过程</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#环境变量"><span class="nav-number">3.3.4.</span> <span class="nav-text">环境变量</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#LD-LIBRARY-PATH"><span class="nav-number">3.3.4.1.</span> <span class="nav-text">LD_LIBRARY_PATH</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#LD-PRELOAD"><span class="nav-number">3.3.4.2.</span> <span class="nav-text">LD_PRELOAD</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#LD-DEBUG"><span class="nav-number">3.3.4.3.</span> <span class="nav-text">LD_DEBUG</span></a></li></ol></li></ol></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#库与运行库"><span class="nav-number">4.</span> <span class="nav-text">库与运行库</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#内存"><span class="nav-number">4.1.</span> <span class="nav-text">内存</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#Segment-fault"><span class="nav-number">4.1.1.</span> <span class="nav-text">Segment fault</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#栈"><span class="nav-number">4.1.2.</span> <span class="nav-text">栈</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#堆"><span class="nav-number">4.1.3.</span> <span class="nav-text">堆</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#mmap"><span class="nav-number">4.1.3.1.</span> <span class="nav-text">mmap</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#堆分配算法"><span class="nav-number">4.1.3.2.</span> <span class="nav-text">堆分配算法</span></a></li></ol></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#运行库"><span class="nav-number">4.2.</span> <span class="nav-text">运行库</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#系统调用与API"><span class="nav-number">4.3.</span> <span class="nav-text">系统调用与API</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#运行库实现"><span class="nav-number">4.4.</span> <span class="nav-text">运行库实现</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#工具"><span class="nav-number">5.</span> <span class="nav-text">工具</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#gcc"><span class="nav-number">5.1.</span> <span class="nav-text">gcc</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#fno-builtin"><span class="nav-number">5.1.1.</span> <span class="nav-text">-fno-builtin</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#objdump"><span class="nav-number">5.2.</span> <span class="nav-text">objdump</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#objcopy"><span class="nav-number">5.3.</span> <span class="nav-text">objcopy</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#readelf"><span class="nav-number">5.4.</span> <span class="nav-text">readelf</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#nm"><span class="nav-number">5.5.</span> <span class="nav-text">nm</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#strip"><span class="nav-number">5.6.</span> <span class="nav-text">strip</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#ar"><span class="nav-number">5.7.</span> <span class="nav-text">ar</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#ld"><span class="nav-number">5.8.</span> <span class="nav-text">ld</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#ldd"><span class="nav-number">5.9.</span> <span class="nav-text">ldd</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#ldconfig"><span class="nav-number">5.10.</span> <span class="nav-text">ldconfig</span></a></li></ol></li></ol></div></div><div class="site-overview-wrap sidebar-panel"><div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person"> <img class="site-author-image" itemprop="image" alt="Winddoing" src="/images/Winddoing.jpg"><p class="site-author-name" itemprop="name">Winddoing</p><div class="site-description" itemprop="description">失败缘于忽视细处，成功始于重视小事。</div></div><div class="site-state-wrap motion-element"><nav class="site-state"><div class="site-state-item site-state-posts"> <a href="/archives/"><span class="site-state-item-count">248</span> <span class="site-state-item-name">日志</span></a></div><div class="site-state-item site-state-categories"> <a href="/categories/"><span class="site-state-item-count">57</span> <span class="site-state-item-name">分类</span></a></div><div class="site-state-item site-state-tags"> <a href="/tags/"><span class="site-state-item-count">214</span> <span class="site-state-item-name">标签</span></a></div></nav></div><div class="links-of-author motion-element"><span class="links-of-author-item"><a href="https://github.com/Winddoing" title="GitHub → https:&#x2F;&#x2F;github.com&#x2F;Winddoing" rel="noopener" target="_blank"><i class="fa fa-fw fa-github"></i> GitHub</a></span><span class="links-of-author-item"><a href="https://gitee.com/winddoing" title="Gitee → https:&#x2F;&#x2F;gitee.com&#x2F;winddoing" rel="noopener" target="_blank"><i class="fa fa-fw fa-codiepie"></i> Gitee</a></span><span class="links-of-author-item"><a href="mailto:winddoing@sina.cn" title="E-Mail → mailto:winddoing@sina.cn" rel="noopener" target="_blank"><i class="fa fa-fw fa-envelope"></i> E-Mail</a></span><span class="links-of-author-item"><a href="https://stackoverflow.com/users/9567361/winddoing" title="StackOverflow → https:&#x2F;&#x2F;stackoverflow.com&#x2F;users&#x2F;9567361&#x2F;winddoing" rel="noopener" target="_blank"><i class="fa fa-fw fa-stack-overflow"></i> StackOverflow</a></span><span class="links-of-author-item"><a href="https://travis-ci.org/Winddoing/Winddoing.github.io" title="Travis CI → https:&#x2F;&#x2F;travis-ci.org&#x2F;Winddoing&#x2F;Winddoing.github.io" rel="noopener" target="_blank"><i class="fa fa-fw fa-terminal"></i> Travis CI</a></span></div><div class="cc-license motion-element" itemprop="license"> <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" class="cc-opacity" rel="noopener" target="_blank"><img src="/images/cc-by-nc-sa.svg" alt="Creative Commons"></a></div><div class="links-of-blogroll motion-element"><div class="links-of-blogroll-title"><i class="fa fa-fw fa-link"></i> Links</div><ul class="links-of-blogroll-list"><li class="links-of-blogroll-item"> <a href="https://winddoing.gitbooks.io/embedded_notes/content/" title="https:&#x2F;&#x2F;winddoing.gitbooks.io&#x2F;embedded_notes&#x2F;content&#x2F;" rel="noopener" target="_blank">嵌入式相关</a></li><li class="links-of-blogroll-item"> <a href="http://blog.csdn.net/sdreamq" title="http:&#x2F;&#x2F;blog.csdn.net&#x2F;sdreamq" rel="noopener" target="_blank">CSDN</a></li><li class="links-of-blogroll-item"> <a href="http://www.wowotech.net/" title="http:&#x2F;&#x2F;www.wowotech.net&#x2F;" rel="noopener" target="_blank">蜗窝科技</a></li><li class="links-of-blogroll-item"> <a href="https://blog.csdn.net/xiongxianze" title="https:&#x2F;&#x2F;blog.csdn.net&#x2F;xiongxianze" rel="noopener" target="_blank">xiongxianze</a></li></ul></div><div id="days"></div><script>function show_date_time(){window.setTimeout("show_date_time()",1e3),BirthDay=new Date("02/26/2014 15:00:00"),today=new Date,timeold=today.getTime()-BirthDay.getTime(),sectimeold=timeold/1e3,secondsold=Math.floor(sectimeold),msPerDay=864e5,e_daysold=timeold/msPerDay,daysold=Math.floor(e_daysold),e_hrsold=24*(e_daysold-daysold),hrsold=setzero(Math.floor(e_hrsold)),e_minsold=60*(e_hrsold-hrsold),minsold=setzero(Math.floor(60*(e_hrsold-hrsold))),seconds=setzero(Math.floor(60*(e_minsold-minsold))),document.getElementById("days").innerHTML="已运行"+daysold+"天"+hrsold+"小时"+minsold+"分"+seconds+"秒"}function setzero(e){return e<10&&(e="0"+e),e}show_date_time()</script></div></div></aside><div id="sidebar-dimmer"></div></div></main><footer class="footer"><div class="footer-inner"><div class="copyright"> &copy; 2014 – <span itemprop="copyrightYear">2020</span><span class="with-love" id="heart"><i class="fa fa-heartbeat"></i></span> <span class="author" itemprop="copyrightHolder">Winddoing</span> <span class="post-meta-divider">|</span><span class="post-meta-item-icon"><i class="fa fa-area-chart"></i></span> <span title="站点总字数">652k</span> <span class="post-meta-divider">|</span><span class="post-meta-item-icon"><i class="fa fa-coffee"></i></span> <span title="站点阅读时长">9:53</span></div><div class="powered-by">由 <a href="https://hexo.io/" class="theme-link" rel="noopener" target="_blank">Hexo</a> 强力驱动 v3.9.0</div> <span class="post-meta-divider">|</span><div class="theme-info">主题 – <a href="https://mist.theme-next.org/" class="theme-link" rel="noopener" target="_blank">NexT.Mist</a> v7.7.0</div><div class="busuanzi-count"><script async src="https://busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script><span class="post-meta-item" id="busuanzi_container_site_uv" style="display:none"><span class="post-meta-item-icon"><i class="fa fa-user"></i></span><span class="site-uv" title="总访客量"><span id="busuanzi_value_site_uv"></span></span></span> <span class="post-meta-divider">|</span><span class="post-meta-item" id="busuanzi_container_site_pv" style="display:none"><span class="post-meta-item-icon"><i class="fa fa-eye"></i></span><span class="site-pv" title="总访问量"><span id="busuanzi_value_site_pv"></span></span></span></div><div style="display:inline"><script type="text/javascript">var cnzz_protocol="https:"==document.location.protocol?" https://":" http://";document.write(unescape("%3Cspan id='cnzz_stat_icon_1254703532'%3E%3C/span%3E%3Cscript src='"+cnzz_protocol+"s95.cnzz.com/z_stat.php%3Fid%3D1254703532%26show%3Dpic' type='text/javascript'%3E%3C/script%3E"))</script></div><script>
  function leancloudSelector(url) {
    url = encodeURI(url);
    return document.getElementById(url).querySelector('.leancloud-visitors-count');
  }
  if (CONFIG.page.isPost) {
    function addCount(Counter) {
      var visitors = document.querySelector('.leancloud_visitors');
      var url = decodeURI(visitors.getAttribute('id'));
      var title = visitors.getAttribute('data-flag-title');

      Counter('get', `/classes/Counter?where=${JSON.stringify({ url })}`)
        .then(response => response.json())
        .then(({ results }) => {
          if (results.length > 0) {
            var counter = results[0];
            Counter('put', '/classes/Counter/' + counter.objectId, { time: { '__op': 'Increment', 'amount': 1 } })
              .then(response => response.json())
              .then(() => {
                leancloudSelector(url).innerText = counter.time + 1;
              })
              .catch(error => {
                console.error('Failed to save visitor count', error);
              })
          } else {
              Counter('post', '/classes/Counter', { title: title, url: url, time: 1 })
                .then(response => response.json())
                .then(() => {
                  leancloudSelector(url).innerText = 1;
                })
                .catch(error => {
                  console.error('Failed to create', error);
                });
          }
        })
        .catch(error => {
          console.error('LeanCloud Counter Error', error);
        });
    }
  } else {
    function showTime(Counter) {
      var visitors = document.querySelectorAll('.leancloud_visitors');
      var entries = [...visitors].map(element => {
        return decodeURI(element.getAttribute('id'));
      });

      Counter('get', `/classes/Counter?where=${JSON.stringify({ url: { '$in': entries } })}`)
        .then(response => response.json())
        .then(({ results }) => {
          if (results.length === 0) {
            document.querySelectorAll('.leancloud_visitors .leancloud-visitors-count').forEach(element => {
              element.innerText = 0;
            });
            return;
          }
          for (let item of results) {
            let { url, time } = item;
            leancloudSelector(url).innerText = time;
          }
          for (let url of entries) {
            var element = leancloudSelector(url);
            if (element.innerText == '') {
              element.innerText = 0;
            }
          }
        })
        .catch(error => {
          console.error('LeanCloud Counter Error', error);
        });
    }
  }

  fetch('https://app-router.leancloud.cn/2/route?appId=Q8qpjA3fOO7FEUBqcmcQFptF-gzGzoHsz')
    .then(response => response.json())
    .then(({ api_server }) => {
      var Counter = (method, url, data) => {
        return fetch(`https://${api_server}/1.1${url}`, {
          method: method,
          headers: {
            'X-LC-Id': 'Q8qpjA3fOO7FEUBqcmcQFptF-gzGzoHsz',
            'X-LC-Key': 'tgUTq5bX3fVmn916EMRe65eJ',
            'Content-Type': 'application/json',
          },
          body: JSON.stringify(data)
        });
      };
      if (CONFIG.page.isPost) {
        if (CONFIG.hostname !== location.hostname) return;
        addCount(Counter);
      } else if (document.querySelectorAll('.post-title-link').length >= 1) {
        showTime(Counter);
      }
    });
  </script></div></footer></div><script src="/lib/anime.min.js"></script><script src="/lib/velocity/velocity.min.js"></script><script src="/lib/velocity/velocity.ui.min.js"></script><script src="/js/utils.js"></script><script src="/js/motion.js"></script><script src="/js/schemes/muse.js"></script><script src="/js/next-boot.js"></script><script>!function(){var t=document.createElement("script"),e=window.location.protocol.split(":")[0];t.src="https"===e?"https://zz.bdstatic.com/linksubmit/push.js":"http://push.zhanzhang.baidu.com/push.js";var s=document.getElementsByTagName("script")[0];s.parentNode.insertBefore(t,s)}()</script><script src="/js/local-search.js"></script><script>
if (document.querySelectorAll('pre.mermaid').length) {
  NexT.utils.getScript('//cdn.jsdelivr.net/npm/mermaid@8/dist/mermaid.min.js', () => {
    mermaid.initialize({
      theme: 'forest',
      logLevel: 3,
      flowchart: { curve: 'linear' },
      gantt: { axisFormat: '%m/%d/%Y' },
      sequence: { actorMargin: 50 }
    });
  }, window.mermaid);
}
</script><script src="//cdn.jsdelivr.net/gh/theme-next/theme-next-needmoreshare2@1/needsharebutton.min.js"></script><script>pbOptions={iconStyle:"default",boxForm:"horizontal",position:"bottomCenter",networks:"Weibo,Wechat,QQZone,Twitter,Facebook,Evernote"},new needShareButton("#needsharebutton-postbottom",pbOptions)</script></body></html>